From fa7612f0a105e0312eabad0e6676b927260e5d63 Mon Sep 17 00:00:00 2001 From: Dmitri Karpovich Date: Wed, 28 Aug 2024 01:23:12 +0200 Subject: [PATCH 1/8] [bitget] Initial implementation --- pom.xml | 1 + xchange-bitget/http-client.env.json | 5 ++ xchange-bitget/pom.xml | 67 ++++++++++++++++++ .../java/org/knowm/xchange/bitget/Bitget.java | 27 ++++++++ .../knowm/xchange/bitget/BitgetExchange.java | 29 ++++++++ .../BitgetJacksonObjectMapperFactory.java | 24 +++++++ .../knowm/xchange/bitget/config/Config.java | 20 ++++++ .../converter/StringToBooleanConverter.java | 13 ++++ .../converter/StringToCurrencyConverter.java | 13 ++++ .../xchange/bitget/dto/BitgetChainDto.java | 69 +++++++++++++++++++ .../xchange/bitget/dto/BitgetCoinDto.java | 35 ++++++++++ .../xchange/bitget/dto/BitgetResponse.java | 26 +++++++ .../xchange/bitget/dto/BitgetServerTime.java | 17 +++++ .../bitget/service/BitgetBaseService.java | 24 +++++++ .../service/BitgetMarketDataService.java | 36 ++++++++++ .../service/BitgetMarketDataServiceRaw.java | 27 ++++++++ xchange-bitget/src/main/resources/bitget.json | 5 ++ .../BitgetMarketDataServiceIntegration.java | 31 +++++++++ ...BitgetMarketDataServiceRawIntegration.java | 47 +++++++++++++ xchange-bitget/src/test/resources/logback.xml | 23 +++++++ .../src/test/resources/rest/common.http | 3 + .../src/test/resources/rest/spot.http | 8 +++ 22 files changed, 550 insertions(+) create mode 100644 xchange-bitget/http-client.env.json create mode 100644 xchange-bitget/pom.xml create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/BitgetJacksonObjectMapperFactory.java create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/Config.java create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/converter/StringToBooleanConverter.java create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/converter/StringToCurrencyConverter.java create mode 100755 xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetChainDto.java create mode 100755 xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetCoinDto.java create mode 100755 xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetResponse.java create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetServerTime.java create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetBaseService.java create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataService.java create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java create mode 100644 xchange-bitget/src/main/resources/bitget.json create mode 100644 xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceIntegration.java create mode 100644 xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRawIntegration.java create mode 100644 xchange-bitget/src/test/resources/logback.xml create mode 100644 xchange-bitget/src/test/resources/rest/common.http create mode 100644 xchange-bitget/src/test/resources/rest/spot.http diff --git a/pom.xml b/pom.xml index 405079c72f7..ec7bc6a7de3 100644 --- a/pom.xml +++ b/pom.xml @@ -50,6 +50,7 @@ xchange-bitcointoyou xchange-bitfinex xchange-bitflyer + xchange-bitget xchange-bithumb xchange-bitmex xchange-bitso diff --git a/xchange-bitget/http-client.env.json b/xchange-bitget/http-client.env.json new file mode 100644 index 00000000000..82e88360d16 --- /dev/null +++ b/xchange-bitget/http-client.env.json @@ -0,0 +1,5 @@ +{ + "default": { + "api_host": "https://api.bitget.com" + } +} \ No newline at end of file diff --git a/xchange-bitget/pom.xml b/xchange-bitget/pom.xml new file mode 100644 index 00000000000..2b9a8eec7f1 --- /dev/null +++ b/xchange-bitget/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + org.knowm.xchange + xchange-parent + 5.2.1-SNAPSHOT + + + xchange-bitget + + XChange Bitget + XChange implementation for the Bitget Exchange + + http://knowm.org/open-source/xchange/ + 2012 + + + Knowm Inc. + http://knowm.org/open-source/xchange/ + + + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${version.fasterxml} + + + + org.junit.jupiter + junit-jupiter-engine + test + + + + org.knowm.xchange + xchange-core + ${project.version} + + + + org.mockito + mockito-junit-jupiter + test + + + + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + integration-test.env.properties + + + + + + + + diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java new file mode 100644 index 00000000000..2add79b2127 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java @@ -0,0 +1,27 @@ +package org.knowm.xchange.bitget; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import java.io.IOException; +import java.util.List; +import org.knowm.xchange.bitget.dto.BitgetCoinDto; +import org.knowm.xchange.bitget.dto.BitgetResponse; +import org.knowm.xchange.bitget.dto.BitgetServerTime; + +@Path("") +@Produces(MediaType.APPLICATION_JSON) +public interface Bitget { + + @GET + @Path("api/v2/public/time") + BitgetResponse serverTime() throws IOException; + + + @GET + @Path("api/v2/spot/public/coins") + BitgetResponse> coinDtos() throws IOException; + + +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java new file mode 100644 index 00000000000..786ebbb7360 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java @@ -0,0 +1,29 @@ +package org.knowm.xchange.bitget; + +import java.io.IOException; +import org.knowm.xchange.BaseExchange; +import org.knowm.xchange.ExchangeSpecification; +import org.knowm.xchange.bitget.service.BitgetMarketDataService; + +public class BitgetExchange extends BaseExchange { + + @Override + protected void initServices() { +// accountService = new BitgetAccountService(this); + marketDataService = new BitgetMarketDataService(this); +// tradeService = new BitgetTradeService(this); + } + + @Override + public ExchangeSpecification getDefaultExchangeSpecification() { + ExchangeSpecification specification = new ExchangeSpecification(getClass()); + specification.setSslUri("https://api.bitget.com"); + specification.setHost("www.bitget.com"); + specification.setExchangeName("Bitget"); + return specification; + } + + @Override + public void remoteInit() throws IOException { + } +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/BitgetJacksonObjectMapperFactory.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/BitgetJacksonObjectMapperFactory.java new file mode 100644 index 00000000000..51d95ab13d9 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/BitgetJacksonObjectMapperFactory.java @@ -0,0 +1,24 @@ +package org.knowm.xchange.bitget.config; + +import com.fasterxml.jackson.annotation.JsonInclude; +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 BitgetJacksonObjectMapperFactory 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); + + // don't write nulls + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + // enable parsing to Instant + objectMapper.registerModule(new JavaTimeModule()); + } +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/Config.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/Config.java new file mode 100644 index 00000000000..f9670089593 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/Config.java @@ -0,0 +1,20 @@ +package org.knowm.xchange.bitget.config; + +import java.time.Clock; +import lombok.Data; + +@Data +public final class Config { + + private Clock clock; + + private static Config instance = new Config(); + + private Config() { + clock = Clock.systemDefaultZone(); + } + + public static Config getInstance() { + return instance; + } +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/converter/StringToBooleanConverter.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/converter/StringToBooleanConverter.java new file mode 100644 index 00000000000..abf89d8cc41 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/converter/StringToBooleanConverter.java @@ -0,0 +1,13 @@ +package org.knowm.xchange.bitget.config.converter; + +import com.fasterxml.jackson.databind.util.StdConverter; +import org.apache.commons.lang3.BooleanUtils; + +/** Converts string value to {@code Boolean} */ +public class StringToBooleanConverter extends StdConverter { + + @Override + public Boolean convert(final String value) { + return BooleanUtils.toBoolean(value); + } +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/converter/StringToCurrencyConverter.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/converter/StringToCurrencyConverter.java new file mode 100644 index 00000000000..00e2a5e870a --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/converter/StringToCurrencyConverter.java @@ -0,0 +1,13 @@ +package org.knowm.xchange.bitget.config.converter; + +import com.fasterxml.jackson.databind.util.StdConverter; +import org.knowm.xchange.currency.Currency; + +/** Converts string value to {@code Currency} */ +public class StringToCurrencyConverter extends StdConverter { + + @Override + public Currency convert(String value) { + return Currency.getInstance(value); + } +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetChainDto.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetChainDto.java new file mode 100755 index 00000000000..9980a05963c --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetChainDto.java @@ -0,0 +1,69 @@ +package org.knowm.xchange.bitget.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; + +@Data +@Builder +@Jacksonized +public class BitgetChainDto { + + @JsonProperty("chain") + private String chain; + + @JsonProperty("needTag") + private Boolean needTag; + + @JsonProperty("withdrawable") + private Boolean isWithdrawEnabled; + + @JsonProperty("rechargeable") + private Boolean isDepositEnabled; + + @JsonProperty("withdrawFee") + private BigDecimal withdrawFee; + + @JsonProperty("extraWithdrawFee") + private BigDecimal extraWithdrawFee; + + @JsonProperty("depositConfirm") + private Integer depositConfirmBlockCount; + + @JsonProperty("withdrawConfirm") + private Integer withdrawConfirmBlockCount; + + @JsonProperty("minDepositAmount") + private BigDecimal minDepositAmount; + + @JsonProperty("minWithdrawAmount") + private BigDecimal minWithdrawAmount; + + @JsonProperty("browserUrl") + private String browserUrl; + + @JsonProperty("contractAddress") + private String contractAddress; + + @JsonProperty("withdrawStep") + private Integer withdrawStep; + + @JsonProperty("withdrawMinScale") + private Integer withdrawMinScale; + + @JsonProperty("congestion") + private Congestion congestion; + + + public static enum Congestion { + @JsonProperty("normal") + NORMAL, + + @JsonProperty("congested") + CONGESTED; + } + +} + diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetCoinDto.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetCoinDto.java new file mode 100755 index 00000000000..0e2d9d366c7 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetCoinDto.java @@ -0,0 +1,35 @@ +package org.knowm.xchange.bitget.dto; + +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.bitget.config.converter.StringToBooleanConverter; +import org.knowm.xchange.bitget.config.converter.StringToCurrencyConverter; +import org.knowm.xchange.currency.Currency; + +@Data +@Builder +@Jacksonized +public class BitgetCoinDto { + + @JsonProperty("coinId") + private String coinId; + + @JsonProperty("coin") + @JsonDeserialize(converter = StringToCurrencyConverter.class) + private Currency currency; + + @JsonProperty("transfer") + private Boolean canTransfer; + + @JsonProperty("areaCoin") + @JsonDeserialize(converter = StringToBooleanConverter.class) + private Boolean isAreaCoin; + + @JsonProperty("chains") + private List chains; + +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetResponse.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetResponse.java new file mode 100755 index 00000000000..765032705d3 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetResponse.java @@ -0,0 +1,26 @@ +package org.knowm.xchange.bitget.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.Instant; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; + +@Data +@Builder +@Jacksonized +public class BitgetResponse { + + @JsonProperty("code") + private String code; + + @JsonProperty("msg") + private String message; + + @JsonProperty("requestTime") + private Instant requestTime; + + @JsonProperty("data") + private T data; + +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetServerTime.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetServerTime.java new file mode 100644 index 00000000000..70a45b4982e --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetServerTime.java @@ -0,0 +1,17 @@ +package org.knowm.xchange.bitget.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.Instant; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; + +@Data +@Builder +@Jacksonized +public class BitgetServerTime { + + @JsonProperty("serverTime") + private Instant serverTime; + +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetBaseService.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetBaseService.java new file mode 100644 index 00000000000..480d0e80a71 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetBaseService.java @@ -0,0 +1,24 @@ +package org.knowm.xchange.bitget.service; + +import org.knowm.xchange.bitget.Bitget; +import org.knowm.xchange.bitget.BitgetExchange; +import org.knowm.xchange.bitget.config.BitgetJacksonObjectMapperFactory; +import org.knowm.xchange.client.ExchangeRestProxyBuilder; +import org.knowm.xchange.service.BaseExchangeService; +import org.knowm.xchange.service.BaseService; + +public class BitgetBaseService extends BaseExchangeService implements BaseService { + + protected final String apiKey; + protected final Bitget bitget; + + public BitgetBaseService(BitgetExchange exchange) { + super(exchange); + bitget = ExchangeRestProxyBuilder + .forInterface(Bitget.class, exchange.getExchangeSpecification()) + .clientConfigCustomizer(clientConfig -> clientConfig.setJacksonObjectMapperFactory(new BitgetJacksonObjectMapperFactory())) + .build(); + apiKey = exchange.getExchangeSpecification().getApiKey(); + + } +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataService.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataService.java new file mode 100644 index 00000000000..e9a0ac43994 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataService.java @@ -0,0 +1,36 @@ +package org.knowm.xchange.bitget.service; + +import java.io.IOException; +import java.time.Duration; +import java.time.Instant; +import org.knowm.xchange.bitget.BitgetExchange; +import org.knowm.xchange.bitget.config.Config; +import org.knowm.xchange.dto.meta.ExchangeHealth; +import org.knowm.xchange.service.marketdata.MarketDataService; + +public class BitgetMarketDataService extends BitgetMarketDataServiceRaw implements + MarketDataService { + + public BitgetMarketDataService(BitgetExchange exchange) { + super(exchange); + } + + @Override + public ExchangeHealth getExchangeHealth() { + try { + Instant serverTime = getBitgetServerTime().getServerTime(); + Instant localTime = Instant.now(Config.getInstance().getClock()); + + // timestamps shouldn't diverge by more than 10 minutes + if (Duration.between(serverTime, localTime).toMinutes() < 10) { + return ExchangeHealth.ONLINE; + } +// } catch (BitgetException | IOException e) { + } catch (IOException e) { + return ExchangeHealth.OFFLINE; + } + + return ExchangeHealth.OFFLINE; + } + +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java new file mode 100644 index 00000000000..013cbc09e58 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java @@ -0,0 +1,27 @@ +package org.knowm.xchange.bitget.service; + +import java.io.IOException; +import java.util.List; +import org.knowm.xchange.bitget.BitgetExchange; +import org.knowm.xchange.bitget.dto.BitgetCoinDto; +import org.knowm.xchange.bitget.dto.BitgetServerTime; + +public class BitgetMarketDataServiceRaw extends BitgetBaseService { + + + public BitgetMarketDataServiceRaw(BitgetExchange exchange) { + super(exchange); + } + + + public BitgetServerTime getBitgetServerTime() throws IOException { + return bitget.serverTime().getData(); + } + + + public List getBitgetCoinDtoList() throws IOException { + return bitget.coinDtos().getData(); + } + + +} diff --git a/xchange-bitget/src/main/resources/bitget.json b/xchange-bitget/src/main/resources/bitget.json new file mode 100644 index 00000000000..2541d0ebf95 --- /dev/null +++ b/xchange-bitget/src/main/resources/bitget.json @@ -0,0 +1,5 @@ +{ + "currency_pairs" : {}, + "currencies" : {}, + "share_rate_limits" : false +} diff --git a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceIntegration.java b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceIntegration.java new file mode 100644 index 00000000000..13b4efe0234 --- /dev/null +++ b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceIntegration.java @@ -0,0 +1,31 @@ +package org.knowm.xchange.bitget.service; + +import static org.assertj.core.api.Assumptions.assumeThat; + +import java.io.IOException; +import java.time.Instant; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.knowm.xchange.ExchangeFactory; +import org.knowm.xchange.bitget.BitgetExchange; +import org.knowm.xchange.dto.meta.ExchangeHealth; + +class BitgetMarketDataServiceIntegration { + + BitgetExchange exchange = ExchangeFactory.INSTANCE.createExchange(BitgetExchange.class); + + + @BeforeEach + void exchange_online() { + // skip if offline + assumeThat(exchange.getMarketDataService().getExchangeHealth()).isEqualTo(ExchangeHealth.ONLINE); + } + + + @Test + void server_time() throws IOException { + Instant a = ((BitgetMarketDataServiceRaw) exchange.getMarketDataService()).getBitgetServerTime().getServerTime(); + System.out.println(a); + } + +} \ No newline at end of file diff --git a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRawIntegration.java b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRawIntegration.java new file mode 100644 index 00000000000..a9278ae2dc8 --- /dev/null +++ b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRawIntegration.java @@ -0,0 +1,47 @@ +package org.knowm.xchange.bitget.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; + +import java.io.IOException; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.knowm.xchange.ExchangeFactory; +import org.knowm.xchange.bitget.BitgetExchange; +import org.knowm.xchange.bitget.dto.BitgetCoinDto; +import org.knowm.xchange.dto.meta.ExchangeHealth; + +class BitgetMarketDataServiceRawIntegration { + + BitgetExchange exchange = ExchangeFactory.INSTANCE.createExchange(BitgetExchange.class); + BitgetMarketDataServiceRaw bitgetMarketDataServiceRaw = (BitgetMarketDataServiceRaw) exchange.getMarketDataService(); + + @BeforeEach + void exchange_online() { + // skip if offline + assumeThat(exchange.getMarketDataService().getExchangeHealth()).isEqualTo( + ExchangeHealth.ONLINE); + } + + + @Test + void valid_coins() throws IOException { + List coins = bitgetMarketDataServiceRaw.getBitgetCoinDtoList(); + + assertThat(coins).isNotEmpty(); + + // validate coins + assertThat(coins).allSatisfy(coin -> { + assertThat(coin.getCoinId()).isNotNull(); + assertThat(coin.getCurrency()).isNotNull(); + + // validate each chain + assertThat(coin.getChains()).allSatisfy(chain -> { + assertThat(chain.getChain()).isNotNull(); + }); + + }); + } + +} \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/logback.xml b/xchange-bitget/src/test/resources/logback.xml new file mode 100644 index 00000000000..c823ac40a99 --- /dev/null +++ b/xchange-bitget/src/test/resources/logback.xml @@ -0,0 +1,23 @@ + + + + + + + + + %d{HH:mm:ss.SSS} [%contextName] [%thread] %-5level %logger{36} - %msg %xEx%n + + + + + + + + + + + + + + diff --git a/xchange-bitget/src/test/resources/rest/common.http b/xchange-bitget/src/test/resources/rest/common.http new file mode 100644 index 00000000000..41acdbe899f --- /dev/null +++ b/xchange-bitget/src/test/resources/rest/common.http @@ -0,0 +1,3 @@ +### Get Server Time +GET {{api_host}}/api/v2/public/time + diff --git a/xchange-bitget/src/test/resources/rest/spot.http b/xchange-bitget/src/test/resources/rest/spot.http new file mode 100644 index 00000000000..38cb59f2626 --- /dev/null +++ b/xchange-bitget/src/test/resources/rest/spot.http @@ -0,0 +1,8 @@ +### Get Coin Info +GET {{api_host}}/api/v2/spot/public/coins + + +### Get Symbol Info +GET {{api_host}}/api/v2/spot/public/symbols + + From c7f5a4165dfdc84fb227e79b8b44c1348b602ff3 Mon Sep 17 00:00:00 2001 From: Dmitri Karpovich Date: Thu, 29 Aug 2024 00:06:08 +0200 Subject: [PATCH 2/8] [bitget] Add symbols metadata --- .../java/org/knowm/xchange/bitget/Bitget.java | 9 +- .../knowm/xchange/bitget/BitgetAdapters.java | 46 ++++++++++ .../knowm/xchange/bitget/BitgetExchange.java | 26 ++++++ .../xchange/bitget/dto/BitgetSymbolDto.java | 86 +++++++++++++++++++ .../service/BitgetMarketDataServiceRaw.java | 10 ++- .../bitget/BitgetExchangeIntegration.java | 35 ++++++++ ...BitgetMarketDataServiceRawIntegration.java | 35 ++++++++ 7 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java create mode 100755 xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetSymbolDto.java create mode 100644 xchange-bitget/src/test/java/org/knowm/xchange/bitget/BitgetExchangeIntegration.java diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java index 2add79b2127..86bca690de3 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java @@ -3,12 +3,14 @@ import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; 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.bitget.dto.BitgetCoinDto; import org.knowm.xchange.bitget.dto.BitgetResponse; import org.knowm.xchange.bitget.dto.BitgetServerTime; +import org.knowm.xchange.bitget.dto.BitgetSymbolDto; @Path("") @Produces(MediaType.APPLICATION_JSON) @@ -21,7 +23,12 @@ public interface Bitget { @GET @Path("api/v2/spot/public/coins") - BitgetResponse> coinDtos() throws IOException; + BitgetResponse> coins() throws IOException; + + + @GET + @Path("api/v2/spot/public/symbols") + BitgetResponse> symbols(@QueryParam("symbol") String symbol) throws IOException; } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java new file mode 100644 index 00000000000..d87285d48fe --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java @@ -0,0 +1,46 @@ +package org.knowm.xchange.bitget; + +import java.util.HashMap; +import java.util.Map; +import lombok.experimental.UtilityClass; +import org.knowm.xchange.bitget.dto.BitgetSymbolDto; +import org.knowm.xchange.bitget.dto.BitgetSymbolDto.Status; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.meta.InstrumentMetaData; +import org.knowm.xchange.instrument.Instrument; + +@UtilityClass +public class BitgetAdapters { + + private final Map SYMBOL_TO_CURRENCY_PAIR = new HashMap<>(); + + + public String toString(Instrument instrument) { + return instrument == null ? null : instrument.getBase().toString() + instrument.getCounter().toString(); + } + + + public void putSymbolMapping(String symbol, CurrencyPair currencyPair) { + SYMBOL_TO_CURRENCY_PAIR.put(symbol, currencyPair); + } + + + public InstrumentMetaData toInstrumentMetaData(BitgetSymbolDto bitgetSymbolDto) { + InstrumentMetaData.Builder builder = new InstrumentMetaData.Builder() + .tradingFee(bitgetSymbolDto.getTakerFeeRate()) + .minimumAmount(bitgetSymbolDto.getMinTradeAmount()) + .maximumAmount(bitgetSymbolDto.getMaxTradeAmount()) + .volumeScale(bitgetSymbolDto.getQuantityPrecision()) + .priceScale(bitgetSymbolDto.getPricePrecision()) + .marketOrderEnabled(bitgetSymbolDto.getStatus() == Status.ONLINE); + + // set min quote amount for USDT + if (bitgetSymbolDto.getCurrencyPair().getCounter().equals(Currency.USDT)) { + builder.counterMinimumAmount(bitgetSymbolDto.getMinTradeUSDT()); + } + + return builder.build(); + } + +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java index 786ebbb7360..0718abb193d 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java @@ -1,9 +1,17 @@ package org.knowm.xchange.bitget; import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import org.knowm.xchange.BaseExchange; import org.knowm.xchange.ExchangeSpecification; +import org.knowm.xchange.bitget.dto.BitgetSymbolDto; import org.knowm.xchange.bitget.service.BitgetMarketDataService; +import org.knowm.xchange.bitget.service.BitgetMarketDataServiceRaw; +import org.knowm.xchange.dto.meta.ExchangeMetaData; +import org.knowm.xchange.dto.meta.InstrumentMetaData; +import org.knowm.xchange.instrument.Instrument; public class BitgetExchange extends BaseExchange { @@ -25,5 +33,23 @@ public ExchangeSpecification getDefaultExchangeSpecification() { @Override public void remoteInit() throws IOException { + BitgetMarketDataServiceRaw bitgetMarketDataServiceRaw = (BitgetMarketDataServiceRaw) marketDataService; + + // initialize symbol mappings + List bitgetSymbolDtos = bitgetMarketDataServiceRaw.getBitgetSymbolDtos(null); + bitgetSymbolDtos.forEach(bitgetSymbolDto -> { + BitgetAdapters.putSymbolMapping(bitgetSymbolDto.getSymbol(), bitgetSymbolDto.getCurrencyPair()); + }); + + + // initialize instrument metadata + Map instruments = bitgetSymbolDtos.stream() + .collect(Collectors.toMap( + BitgetSymbolDto::getCurrencyPair, + BitgetAdapters::toInstrumentMetaData) + ); + + exchangeMetaData = new ExchangeMetaData(instruments, null, null, null, null); + } } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetSymbolDto.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetSymbolDto.java new file mode 100755 index 00000000000..abca536daa3 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetSymbolDto.java @@ -0,0 +1,86 @@ +package org.knowm.xchange.bitget.dto; + +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.bitget.config.converter.StringToBooleanConverter; +import org.knowm.xchange.bitget.config.converter.StringToCurrencyConverter; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.currency.CurrencyPair; + +@Data +@Builder +@Jacksonized +public class BitgetSymbolDto { + + @JsonProperty("symbol") + private String symbol; + + @JsonProperty("baseCoin") + @JsonDeserialize(converter = StringToCurrencyConverter.class) + private Currency base; + + @JsonProperty("quoteCoin") + @JsonDeserialize(converter = StringToCurrencyConverter.class) + private Currency quote; + + @JsonProperty("minTradeAmount") + private BigDecimal minTradeAmount; + + @JsonProperty("maxTradeAmount") + private BigDecimal maxTradeAmount; + + @JsonProperty("takerFeeRate") + private BigDecimal takerFeeRate; + + @JsonProperty("makerFeeRate") + private BigDecimal makerFeeRate; + + @JsonProperty("pricePrecision") + private Integer pricePrecision; + + @JsonProperty("quantityPrecision") + private Integer quantityPrecision; + + @JsonProperty("quotePrecision") + private Integer quotePrecision; + + @JsonProperty("status") + private Status status; + + @JsonProperty("minTradeUSDT") + private BigDecimal minTradeUSDT; + + @JsonProperty("buyLimitPriceRatio") + private BigDecimal buyLimitPriceRatio; + + @JsonProperty("sellLimitPriceRatio") + private BigDecimal sellLimitPriceRatio; + + @JsonProperty("areaSymbol") + @JsonDeserialize(converter = StringToBooleanConverter.class) + private Boolean isAreaSymbol; + + + public CurrencyPair getCurrencyPair() { + return new CurrencyPair(base, quote); + } + + + public static enum Status { + @JsonProperty("offline") + OFFLINE, + + @JsonProperty("gray") + GRAY, + + @JsonProperty("online") + ONLINE, + + @JsonProperty("halt") + HALT + } +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java index 013cbc09e58..5a63907d430 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java @@ -2,9 +2,12 @@ import java.io.IOException; import java.util.List; +import org.knowm.xchange.bitget.BitgetAdapters; import org.knowm.xchange.bitget.BitgetExchange; import org.knowm.xchange.bitget.dto.BitgetCoinDto; import org.knowm.xchange.bitget.dto.BitgetServerTime; +import org.knowm.xchange.bitget.dto.BitgetSymbolDto; +import org.knowm.xchange.instrument.Instrument; public class BitgetMarketDataServiceRaw extends BitgetBaseService { @@ -20,7 +23,12 @@ public BitgetServerTime getBitgetServerTime() throws IOException { public List getBitgetCoinDtoList() throws IOException { - return bitget.coinDtos().getData(); + return bitget.coins().getData(); + } + + + public List getBitgetSymbolDtos(Instrument instrument) throws IOException { + return bitget.symbols(BitgetAdapters.toString(instrument)).getData(); } diff --git a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/BitgetExchangeIntegration.java b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/BitgetExchangeIntegration.java new file mode 100644 index 00000000000..fa5451b9d1a --- /dev/null +++ b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/BitgetExchangeIntegration.java @@ -0,0 +1,35 @@ +package org.knowm.xchange.bitget; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; +import static org.knowm.xchange.dto.meta.ExchangeHealth.ONLINE; + +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.knowm.xchange.ExchangeFactory; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.meta.InstrumentMetaData; +import org.knowm.xchange.instrument.Instrument; + +class BitgetExchangeIntegration { + + BitgetExchange exchange = ExchangeFactory.INSTANCE.createExchange(BitgetExchange.class); + + @BeforeEach + void exchange_online() { + // skip if offline + assumeThat(exchange.getMarketDataService().getExchangeHealth()).isEqualTo(ONLINE); + } + + + @Test + void valid_metadata() { + assertThat(exchange.getExchangeMetaData()).isNotNull(); + Map instruments = exchange.getExchangeMetaData().getInstruments(); + assertThat(instruments).containsKey(CurrencyPair.BTC_USDT); + } + + + +} \ No newline at end of file diff --git a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRawIntegration.java b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRawIntegration.java index a9278ae2dc8..158ad7b142c 100644 --- a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRawIntegration.java +++ b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRawIntegration.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assumptions.assumeThat; +import static org.knowm.xchange.currency.CurrencyPair.BTC_USDT; import java.io.IOException; import java.util.List; @@ -10,6 +11,7 @@ import org.knowm.xchange.ExchangeFactory; import org.knowm.xchange.bitget.BitgetExchange; import org.knowm.xchange.bitget.dto.BitgetCoinDto; +import org.knowm.xchange.bitget.dto.BitgetSymbolDto; import org.knowm.xchange.dto.meta.ExchangeHealth; class BitgetMarketDataServiceRawIntegration { @@ -44,4 +46,37 @@ void valid_coins() throws IOException { }); } + + @Test + void valid_symbol() throws IOException { + List symbols = bitgetMarketDataServiceRaw.getBitgetSymbolDtos(BTC_USDT); + + assertThat(symbols).hasSize(1); + + BitgetSymbolDto symbol = symbols.get(0); + assertThat(symbol.getCurrencyPair()).isEqualTo(BTC_USDT); + assertThat(symbol.getPricePrecision()).isPositive(); + assertThat(symbol.getQuantityPrecision()).isPositive(); + assertThat(symbol.getQuotePrecision()).isPositive(); + + } + + + @Test + void valid_symbols() throws IOException { + List symbols = bitgetMarketDataServiceRaw.getBitgetSymbolDtos(null); + + assertThat(symbols).isNotEmpty(); + + // validate symbols + assertThat(symbols).allSatisfy(symbol -> { + assertThat(symbol.getCurrencyPair()).isNotNull(); + assertThat(symbol.getBase()).isNotNull(); + assertThat(symbol.getQuote()).isNotNull(); + + }); + + } + + } \ No newline at end of file From f6cc2758691ebaf9dd245bc4d193836dcf43d125 Mon Sep 17 00:00:00 2001 From: Dmitri Karpovich Date: Fri, 30 Aug 2024 00:43:09 +0200 Subject: [PATCH 3/8] [bitget] Add getting of tickers --- xchange-bitget/lombok.config | 2 + .../java/org/knowm/xchange/bitget/Bitget.java | 21 ++++-- .../knowm/xchange/bitget/BitgetAdapters.java | 35 ++++++++- .../xchange/bitget/BitgetErrorAdapter.java | 23 ++++++ .../knowm/xchange/bitget/BitgetExchange.java | 2 +- .../xchange/bitget/dto/BitgetException.java | 26 +++++++ .../xchange/bitget/dto/BitgetResponse.java | 10 ++- .../dto/{ => marketdata}/BitgetChainDto.java | 2 +- .../dto/{ => marketdata}/BitgetCoinDto.java | 2 +- .../{ => marketdata}/BitgetServerTime.java | 2 +- .../dto/{ => marketdata}/BitgetSymbolDto.java | 2 +- .../dto/marketdata/BitgetTickerDto.java | 71 +++++++++++++++++++ .../service/BitgetMarketDataService.java | 45 +++++++++++- .../service/BitgetMarketDataServiceRaw.java | 12 +++- .../bitget/BitgetExchangeIntegration.java | 16 +---- .../bitget/BitgetIntegrationTestParent.java | 26 +++++++ .../BitgetMarketDataServiceIntegration.java | 54 ++++++++++---- ...BitgetMarketDataServiceRawIntegration.java | 21 ++---- .../src/test/resources/rest/spot.http | 4 ++ 19 files changed, 310 insertions(+), 66 deletions(-) create mode 100644 xchange-bitget/lombok.config create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetErrorAdapter.java create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetException.java rename xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/{ => marketdata}/BitgetChainDto.java (96%) rename xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/{ => marketdata}/BitgetCoinDto.java (94%) rename xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/{ => marketdata}/BitgetServerTime.java (85%) rename xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/{ => marketdata}/BitgetSymbolDto.java (97%) create mode 100755 xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetTickerDto.java create mode 100644 xchange-bitget/src/test/java/org/knowm/xchange/bitget/BitgetIntegrationTestParent.java diff --git a/xchange-bitget/lombok.config b/xchange-bitget/lombok.config new file mode 100644 index 00000000000..22b090cc4b6 --- /dev/null +++ b/xchange-bitget/lombok.config @@ -0,0 +1,2 @@ +lombok.equalsAndHashCode.callSuper = call +lombok.tostring.callsuper = call \ No newline at end of file diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java index 86bca690de3..8ed381d755f 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java @@ -7,10 +7,12 @@ import jakarta.ws.rs.core.MediaType; import java.io.IOException; import java.util.List; -import org.knowm.xchange.bitget.dto.BitgetCoinDto; +import org.knowm.xchange.bitget.dto.BitgetException; import org.knowm.xchange.bitget.dto.BitgetResponse; -import org.knowm.xchange.bitget.dto.BitgetServerTime; -import org.knowm.xchange.bitget.dto.BitgetSymbolDto; +import org.knowm.xchange.bitget.dto.marketdata.BitgetCoinDto; +import org.knowm.xchange.bitget.dto.marketdata.BitgetServerTime; +import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto; +import org.knowm.xchange.bitget.dto.marketdata.BitgetTickerDto; @Path("") @Produces(MediaType.APPLICATION_JSON) @@ -18,17 +20,24 @@ public interface Bitget { @GET @Path("api/v2/public/time") - BitgetResponse serverTime() throws IOException; + BitgetResponse serverTime() throws IOException, BitgetException; @GET @Path("api/v2/spot/public/coins") - BitgetResponse> coins() throws IOException; + BitgetResponse> coins() throws IOException, BitgetException; @GET @Path("api/v2/spot/public/symbols") - BitgetResponse> symbols(@QueryParam("symbol") String symbol) throws IOException; + BitgetResponse> symbols(@QueryParam("symbol") String symbol) + throws IOException, BitgetException; + + + @GET + @Path("api/v2/spot/market/tickers") + BitgetResponse> tickers(@QueryParam("symbol") String symbol) + throws IOException, BitgetException; } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java index d87285d48fe..2bf1b428fff 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java @@ -3,10 +3,12 @@ import java.util.HashMap; import java.util.Map; import lombok.experimental.UtilityClass; -import org.knowm.xchange.bitget.dto.BitgetSymbolDto; -import org.knowm.xchange.bitget.dto.BitgetSymbolDto.Status; +import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto; +import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto.Status; +import org.knowm.xchange.bitget.dto.marketdata.BitgetTickerDto; import org.knowm.xchange.currency.Currency; import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.marketdata.Ticker; import org.knowm.xchange.dto.meta.InstrumentMetaData; import org.knowm.xchange.instrument.Instrument; @@ -16,6 +18,11 @@ public class BitgetAdapters { private final Map SYMBOL_TO_CURRENCY_PAIR = new HashMap<>(); + public CurrencyPair toCurrencyPair(String symbol) { + return SYMBOL_TO_CURRENCY_PAIR.get(symbol); + } + + public String toString(Instrument instrument) { return instrument == null ? null : instrument.getBase().toString() + instrument.getCounter().toString(); } @@ -43,4 +50,28 @@ public InstrumentMetaData toInstrumentMetaData(BitgetSymbolDto bitgetSymbolDto) return builder.build(); } + + public Ticker toTicker(BitgetTickerDto bitgetTickerDto) { + CurrencyPair currencyPair = toCurrencyPair(bitgetTickerDto.getSymbol()); + if (currencyPair == null) { + return null; + } + return new Ticker.Builder() + .instrument(currencyPair) + .open(bitgetTickerDto.getOpen24h()) + .last(bitgetTickerDto.getLastPrice()) + .bid(bitgetTickerDto.getBestBidPrice()) + .ask(bitgetTickerDto.getBestAskPrice()) + .high(bitgetTickerDto.getHigh24h()) + .low(bitgetTickerDto.getLow24h()) + .volume(bitgetTickerDto.getAssetVolume24h()) + .quoteVolume(bitgetTickerDto.getQuoteVolume24h()) + .timestamp(bitgetTickerDto.getTimestampAsDate()) + .bidSize(bitgetTickerDto.getBestBidSize()) + .askSize(bitgetTickerDto.getBestAskSize()) + .percentageChange(bitgetTickerDto.getChange24h()) + .build(); + } + + } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetErrorAdapter.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetErrorAdapter.java new file mode 100644 index 00000000000..ca5386d4c89 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetErrorAdapter.java @@ -0,0 +1,23 @@ +package org.knowm.xchange.bitget; + +import lombok.experimental.UtilityClass; +import org.knowm.xchange.bitget.dto.BitgetException; +import org.knowm.xchange.exceptions.ExchangeException; +import org.knowm.xchange.exceptions.InstrumentNotValidException; + +@UtilityClass +public class BitgetErrorAdapter { + + public final int INVALID_PARAMETER_CODE = 40034; + + public ExchangeException adapt(BitgetException e) { + + switch (e.getCode()) { + case INVALID_PARAMETER_CODE: + return new InstrumentNotValidException(e.getMessage(), e); + + default: + return new ExchangeException(e.getMessage(), e); + } + } +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java index 0718abb193d..8fcdae1d861 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java @@ -6,7 +6,7 @@ import java.util.stream.Collectors; import org.knowm.xchange.BaseExchange; import org.knowm.xchange.ExchangeSpecification; -import org.knowm.xchange.bitget.dto.BitgetSymbolDto; +import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto; import org.knowm.xchange.bitget.service.BitgetMarketDataService; import org.knowm.xchange.bitget.service.BitgetMarketDataServiceRaw; import org.knowm.xchange.dto.meta.ExchangeMetaData; diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetException.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetException.java new file mode 100644 index 00000000000..34d33813bd7 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetException.java @@ -0,0 +1,26 @@ +package org.knowm.xchange.bitget.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.Instant; +import lombok.Builder; +import lombok.Value; +import lombok.extern.jackson.Jacksonized; + +@Value +@Builder +@Jacksonized +public class BitgetException extends RuntimeException { + + @JsonProperty("code") + Integer code; + + @JsonProperty("msg") + String message; + + @JsonProperty("requestTime") + Instant requestTime; + + @JsonProperty("data") + Object data; + +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetResponse.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetResponse.java index 765032705d3..82f44b7677f 100755 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetResponse.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetResponse.java @@ -5,6 +5,7 @@ import lombok.Builder; import lombok.Data; import lombok.extern.jackson.Jacksonized; +import si.mazi.rescu.ExceptionalReturnContentException; @Data @Builder @@ -12,7 +13,7 @@ public class BitgetResponse { @JsonProperty("code") - private String code; + private Integer code; @JsonProperty("msg") private String message; @@ -23,4 +24,11 @@ public class BitgetResponse { @JsonProperty("data") private T data; + public void setCode(Integer code) { + if (code != 0) { + throw new ExceptionalReturnContentException(String.valueOf(code)); + } + this.code = code; + } + } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetChainDto.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetChainDto.java similarity index 96% rename from xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetChainDto.java rename to xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetChainDto.java index 9980a05963c..62139145dec 100755 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetChainDto.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetChainDto.java @@ -1,4 +1,4 @@ -package org.knowm.xchange.bitget.dto; +package org.knowm.xchange.bitget.dto.marketdata; import com.fasterxml.jackson.annotation.JsonProperty; import java.math.BigDecimal; diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetCoinDto.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetCoinDto.java similarity index 94% rename from xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetCoinDto.java rename to xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetCoinDto.java index 0e2d9d366c7..60217f85ae8 100755 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetCoinDto.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetCoinDto.java @@ -1,4 +1,4 @@ -package org.knowm.xchange.bitget.dto; +package org.knowm.xchange.bitget.dto.marketdata; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetServerTime.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetServerTime.java similarity index 85% rename from xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetServerTime.java rename to xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetServerTime.java index 70a45b4982e..6af71802610 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetServerTime.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetServerTime.java @@ -1,4 +1,4 @@ -package org.knowm.xchange.bitget.dto; +package org.knowm.xchange.bitget.dto.marketdata; import com.fasterxml.jackson.annotation.JsonProperty; import java.time.Instant; diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetSymbolDto.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetSymbolDto.java similarity index 97% rename from xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetSymbolDto.java rename to xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetSymbolDto.java index abca536daa3..da353d48c2a 100755 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/BitgetSymbolDto.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetSymbolDto.java @@ -1,4 +1,4 @@ -package org.knowm.xchange.bitget.dto; +package org.knowm.xchange.bitget.dto.marketdata; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetTickerDto.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetTickerDto.java new file mode 100755 index 00000000000..d516ba1cb24 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetTickerDto.java @@ -0,0 +1,71 @@ +package org.knowm.xchange.bitget.dto.marketdata; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.Date; +import java.util.Optional; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; + +@Data +@Builder +@Jacksonized +public class BitgetTickerDto { + + @JsonProperty("symbol") + private String symbol; + + @JsonProperty("high24h") + private BigDecimal high24h; + + @JsonProperty("open") + private BigDecimal open24h; + + @JsonProperty("lastPr") + private BigDecimal lastPrice; + + @JsonProperty("low24h") + private BigDecimal low24h; + + @JsonProperty("quoteVolume") + private BigDecimal quoteVolume24h; + + @JsonProperty("baseVolume") + private BigDecimal assetVolume24h; + + @JsonProperty("usdtVolume") + private BigDecimal usdtVolume24h; + + @JsonProperty("bidPr") + private BigDecimal bestBidPrice; + + @JsonProperty("bidSz") + private BigDecimal bestBidSize; + + @JsonProperty("askPr") + private BigDecimal bestAskPrice; + + @JsonProperty("askSz") + private BigDecimal bestAskSize; + + @JsonProperty("openUtc") + private BigDecimal openUtc; + + @JsonProperty("ts") + private Instant timestamp; + + @JsonProperty("changeUtc24h") + private BigDecimal changeUtc24h; + + @JsonProperty("change24h") + private BigDecimal change24h; + + public Date getTimestampAsDate() { + return Optional.ofNullable(timestamp) + .map(Date::from) + .orElse(null); + } + +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataService.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataService.java index e9a0ac43994..07b27338738 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataService.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataService.java @@ -3,10 +3,21 @@ import java.io.IOException; import java.time.Duration; import java.time.Instant; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import org.knowm.xchange.bitget.BitgetAdapters; +import org.knowm.xchange.bitget.BitgetErrorAdapter; import org.knowm.xchange.bitget.BitgetExchange; import org.knowm.xchange.bitget.config.Config; +import org.knowm.xchange.bitget.dto.BitgetException; +import org.knowm.xchange.bitget.dto.marketdata.BitgetTickerDto; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.marketdata.Ticker; import org.knowm.xchange.dto.meta.ExchangeHealth; +import org.knowm.xchange.instrument.Instrument; import org.knowm.xchange.service.marketdata.MarketDataService; +import org.knowm.xchange.service.marketdata.params.Params; public class BitgetMarketDataService extends BitgetMarketDataServiceRaw implements MarketDataService { @@ -25,12 +36,42 @@ public ExchangeHealth getExchangeHealth() { if (Duration.between(serverTime, localTime).toMinutes() < 10) { return ExchangeHealth.ONLINE; } -// } catch (BitgetException | IOException e) { - } catch (IOException e) { + } catch (BitgetException | IOException e) { return ExchangeHealth.OFFLINE; } return ExchangeHealth.OFFLINE; } + + @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 { + try { + List tickers = getBitgetTickerDtos(instrument); + return BitgetAdapters.toTicker(tickers.get(0)); + + } catch (BitgetException e) { + throw BitgetErrorAdapter.adapt(e); + } + } + + + @Override + public List getTickers(Params params) throws IOException { + try { + return getBitgetTickerDtos(null).stream() + .map(BitgetAdapters::toTicker) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + } catch (BitgetException e) { + throw BitgetErrorAdapter.adapt(e); + } + } } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java index 5a63907d430..1e3fe76c977 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java @@ -4,9 +4,10 @@ import java.util.List; import org.knowm.xchange.bitget.BitgetAdapters; import org.knowm.xchange.bitget.BitgetExchange; -import org.knowm.xchange.bitget.dto.BitgetCoinDto; -import org.knowm.xchange.bitget.dto.BitgetServerTime; -import org.knowm.xchange.bitget.dto.BitgetSymbolDto; +import org.knowm.xchange.bitget.dto.marketdata.BitgetCoinDto; +import org.knowm.xchange.bitget.dto.marketdata.BitgetServerTime; +import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto; +import org.knowm.xchange.bitget.dto.marketdata.BitgetTickerDto; import org.knowm.xchange.instrument.Instrument; public class BitgetMarketDataServiceRaw extends BitgetBaseService { @@ -32,4 +33,9 @@ public List getBitgetSymbolDtos(Instrument instrument) throws I } + public List getBitgetTickerDtos(Instrument instrument) throws IOException { + return bitget.tickers(BitgetAdapters.toString(instrument)).getData(); + } + + } diff --git a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/BitgetExchangeIntegration.java b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/BitgetExchangeIntegration.java index fa5451b9d1a..e22fc7207cf 100644 --- a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/BitgetExchangeIntegration.java +++ b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/BitgetExchangeIntegration.java @@ -1,27 +1,14 @@ package org.knowm.xchange.bitget; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assumptions.assumeThat; -import static org.knowm.xchange.dto.meta.ExchangeHealth.ONLINE; import java.util.Map; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.knowm.xchange.ExchangeFactory; import org.knowm.xchange.currency.CurrencyPair; import org.knowm.xchange.dto.meta.InstrumentMetaData; import org.knowm.xchange.instrument.Instrument; -class BitgetExchangeIntegration { - - BitgetExchange exchange = ExchangeFactory.INSTANCE.createExchange(BitgetExchange.class); - - @BeforeEach - void exchange_online() { - // skip if offline - assumeThat(exchange.getMarketDataService().getExchangeHealth()).isEqualTo(ONLINE); - } - +class BitgetExchangeIntegration extends BitgetIntegrationTestParent { @Test void valid_metadata() { @@ -31,5 +18,4 @@ void valid_metadata() { } - } \ No newline at end of file diff --git a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/BitgetIntegrationTestParent.java b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/BitgetIntegrationTestParent.java new file mode 100644 index 00000000000..52bee0a28a6 --- /dev/null +++ b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/BitgetIntegrationTestParent.java @@ -0,0 +1,26 @@ +package org.knowm.xchange.bitget; + +import static org.assertj.core.api.Assumptions.assumeThat; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.knowm.xchange.ExchangeFactory; +import org.knowm.xchange.dto.meta.ExchangeHealth; + +public class BitgetIntegrationTestParent { + + protected static BitgetExchange exchange; + + @BeforeAll + static void init() { + exchange = ExchangeFactory.INSTANCE.createExchange(BitgetExchange.class); + } + + @BeforeEach + void exchange_online() { + // skip if offline + assumeThat(exchange.getMarketDataService().getExchangeHealth()).isEqualTo(ExchangeHealth.ONLINE); + } + + +} diff --git a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceIntegration.java b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceIntegration.java index 13b4efe0234..1e670e45345 100644 --- a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceIntegration.java +++ b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceIntegration.java @@ -1,31 +1,55 @@ package org.knowm.xchange.bitget.service; -import static org.assertj.core.api.Assumptions.assumeThat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import java.io.IOException; -import java.time.Instant; -import org.junit.jupiter.api.BeforeEach; +import java.util.List; import org.junit.jupiter.api.Test; -import org.knowm.xchange.ExchangeFactory; -import org.knowm.xchange.bitget.BitgetExchange; -import org.knowm.xchange.dto.meta.ExchangeHealth; +import org.knowm.xchange.bitget.BitgetIntegrationTestParent; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.marketdata.Ticker; +import org.knowm.xchange.exceptions.InstrumentNotValidException; -class BitgetMarketDataServiceIntegration { +class BitgetMarketDataServiceIntegration extends BitgetIntegrationTestParent { + + @Test + void valid_single_ticker() throws IOException { + Ticker ticker = exchange.getMarketDataService().getTicker(CurrencyPair.BTC_USDT); + + assertThat(ticker.getInstrument()).isEqualTo(CurrencyPair.BTC_USDT); + assertThat(ticker.getLast()).isNotNull(); + + if (ticker.getBid().signum() > 0 && ticker.getAsk().signum() > 0) { + assertThat(ticker.getBid()).isLessThan(ticker.getAsk()); + } + + } - BitgetExchange exchange = ExchangeFactory.INSTANCE.createExchange(BitgetExchange.class); + @Test + void check_exceptions() { + assertThatExceptionOfType(InstrumentNotValidException.class) + .isThrownBy(() -> + exchange.getMarketDataService().getTicker(new CurrencyPair("NONEXISTING/NONEXISTING"))); - @BeforeEach - void exchange_online() { - // skip if offline - assumeThat(exchange.getMarketDataService().getExchangeHealth()).isEqualTo(ExchangeHealth.ONLINE); } @Test - void server_time() throws IOException { - Instant a = ((BitgetMarketDataServiceRaw) exchange.getMarketDataService()).getBitgetServerTime().getServerTime(); - System.out.println(a); + void valid_tickers() throws IOException { + List tickers = exchange.getMarketDataService().getTickers(null); + assertThat(tickers).isNotEmpty(); + + assertThat(tickers).allSatisfy(ticker -> { + assertThat(ticker.getInstrument()).isNotNull(); + assertThat(ticker.getLast()).isNotNull(); + + if (ticker.getBid().signum() > 0 && ticker.getAsk().signum() > 0) { + assertThat(ticker.getBid()).isLessThan(ticker.getAsk()); + } + }); } + } \ No newline at end of file diff --git a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRawIntegration.java b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRawIntegration.java index 158ad7b142c..4a8782e1b91 100644 --- a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRawIntegration.java +++ b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRawIntegration.java @@ -1,32 +1,19 @@ package org.knowm.xchange.bitget.service; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assumptions.assumeThat; import static org.knowm.xchange.currency.CurrencyPair.BTC_USDT; import java.io.IOException; import java.util.List; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.knowm.xchange.ExchangeFactory; -import org.knowm.xchange.bitget.BitgetExchange; -import org.knowm.xchange.bitget.dto.BitgetCoinDto; -import org.knowm.xchange.bitget.dto.BitgetSymbolDto; -import org.knowm.xchange.dto.meta.ExchangeHealth; +import org.knowm.xchange.bitget.BitgetIntegrationTestParent; +import org.knowm.xchange.bitget.dto.marketdata.BitgetCoinDto; +import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto; -class BitgetMarketDataServiceRawIntegration { +class BitgetMarketDataServiceRawIntegration extends BitgetIntegrationTestParent { - BitgetExchange exchange = ExchangeFactory.INSTANCE.createExchange(BitgetExchange.class); BitgetMarketDataServiceRaw bitgetMarketDataServiceRaw = (BitgetMarketDataServiceRaw) exchange.getMarketDataService(); - @BeforeEach - void exchange_online() { - // skip if offline - assumeThat(exchange.getMarketDataService().getExchangeHealth()).isEqualTo( - ExchangeHealth.ONLINE); - } - - @Test void valid_coins() throws IOException { List coins = bitgetMarketDataServiceRaw.getBitgetCoinDtoList(); diff --git a/xchange-bitget/src/test/resources/rest/spot.http b/xchange-bitget/src/test/resources/rest/spot.http index 38cb59f2626..dd85d32d49d 100644 --- a/xchange-bitget/src/test/resources/rest/spot.http +++ b/xchange-bitget/src/test/resources/rest/spot.http @@ -6,3 +6,7 @@ GET {{api_host}}/api/v2/spot/public/coins GET {{api_host}}/api/v2/spot/public/symbols +### Get Ticker Information +GET {{api_host}}/api/v2/spot/market/tickers + + From 6b1eff3b3527391d0745c3b7f91777634de212fe Mon Sep 17 00:00:00 2001 From: Dmitri Karpovich Date: Fri, 30 Aug 2024 20:46:51 +0200 Subject: [PATCH 4/8] [bitget] Add getting of balances --- xchange-bitget/.gitignore | 5 +++ xchange-bitget/README.md | 21 +++++++++ .../example.http-client.private.env.json | 7 +++ .../example.integration-test.env.properties | 3 ++ .../knowm/xchange/bitget/BitgetAdapters.java | 36 ++++++++++++++- .../xchange/bitget/BitgetAuthenticated.java | 31 +++++++++++++ .../knowm/xchange/bitget/BitgetExchange.java | 3 +- .../bitget/dto/account/BitgetBalanceDto.java | 37 +++++++++++++++ .../dto/marketdata/BitgetTickerDto.java | 8 ---- .../bitget/service/BitgetAccountService.java | 33 ++++++++++++++ .../service/BitgetAccountServiceRaw.java | 18 ++++++++ .../bitget/service/BitgetBaseService.java | 11 +++++ .../xchange/bitget/service/BitgetDigest.java | 45 +++++++++++++++++++ .../BitgetAccountServiceIntegration.java | 44 ++++++++++++++++++ .../bitget/service/BitgetDigestTest.java | 40 +++++++++++++++++ .../src/test/resources/rest/sign.js | 22 +++++++++ .../src/test/resources/rest/spot.http | 24 ++++++++++ 17 files changed, 378 insertions(+), 10 deletions(-) create mode 100644 xchange-bitget/.gitignore create mode 100644 xchange-bitget/README.md create mode 100644 xchange-bitget/example.http-client.private.env.json create mode 100644 xchange-bitget/example.integration-test.env.properties create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAuthenticated.java create mode 100755 xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/account/BitgetBalanceDto.java create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetAccountService.java create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetAccountServiceRaw.java create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetDigest.java create mode 100644 xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetAccountServiceIntegration.java create mode 100644 xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetDigestTest.java create mode 100644 xchange-bitget/src/test/resources/rest/sign.js diff --git a/xchange-bitget/.gitignore b/xchange-bitget/.gitignore new file mode 100644 index 00000000000..e0acc9b7bf4 --- /dev/null +++ b/xchange-bitget/.gitignore @@ -0,0 +1,5 @@ +# private properties for idea rest client +http-client.private.env.json + +# private properties for integration tests +integration-test.env.properties \ No newline at end of file diff --git a/xchange-bitget/README.md b/xchange-bitget/README.md new file mode 100644 index 00000000000..f728ba8eda6 --- /dev/null +++ b/xchange-bitget/README.md @@ -0,0 +1,21 @@ +## Using IntelliJ Idea HTTP client + +There are *.http files stored in `src/test/resources/rest` that can be used with IntelliJ Idea HTTP Client. + +Some requests need authorization, so the api credentials have to be stored in `http-client.private.env.json` in module's root. Sample content can be found in `example.http-client.private.env.json` + +> [!CAUTION] +> Never commit your api credentials to the repository! + + +[HTTP Client documentation](https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html) + +## Running integration tests that require API keys + +Integration tests that require API keys read them from environment variables. They can be defined in `integration-test.env.properties`. Sample content can be found in `example.integration-test.env.properties`. + +If no keys are provided the integration tests that need them are skipped. + +> [!CAUTION] +> Never commit your api credentials to the repository! + diff --git a/xchange-bitget/example.http-client.private.env.json b/xchange-bitget/example.http-client.private.env.json new file mode 100644 index 00000000000..6a0f22d9764 --- /dev/null +++ b/xchange-bitget/example.http-client.private.env.json @@ -0,0 +1,7 @@ +{ + "default": { + "api_key": "change_me", + "api_secret": "change_me", + "api_passphrase": "change_me" + } +} \ No newline at end of file diff --git a/xchange-bitget/example.integration-test.env.properties b/xchange-bitget/example.integration-test.env.properties new file mode 100644 index 00000000000..9bd1822a2d5 --- /dev/null +++ b/xchange-bitget/example.integration-test.env.properties @@ -0,0 +1,3 @@ +apiKey=change_me +secretKey=change_me +passphrase=change_me diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java index 2bf1b428fff..2a24178451b 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java @@ -1,13 +1,21 @@ package org.knowm.xchange.bitget; +import java.time.Instant; +import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; import lombok.experimental.UtilityClass; +import org.knowm.xchange.bitget.dto.account.BitgetBalanceDto; import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto; import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto.Status; import org.knowm.xchange.bitget.dto.marketdata.BitgetTickerDto; import org.knowm.xchange.currency.Currency; import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.account.Balance; +import org.knowm.xchange.dto.account.Wallet; import org.knowm.xchange.dto.marketdata.Ticker; import org.knowm.xchange.dto.meta.InstrumentMetaData; import org.knowm.xchange.instrument.Instrument; @@ -66,7 +74,7 @@ public Ticker toTicker(BitgetTickerDto bitgetTickerDto) { .low(bitgetTickerDto.getLow24h()) .volume(bitgetTickerDto.getAssetVolume24h()) .quoteVolume(bitgetTickerDto.getQuoteVolume24h()) - .timestamp(bitgetTickerDto.getTimestampAsDate()) + .timestamp(toDate(bitgetTickerDto.getTimestamp())) .bidSize(bitgetTickerDto.getBestBidSize()) .askSize(bitgetTickerDto.getBestAskSize()) .percentageChange(bitgetTickerDto.getChange24h()) @@ -74,4 +82,30 @@ public Ticker toTicker(BitgetTickerDto bitgetTickerDto) { } + public Date toDate(Instant instant) { + return Optional.ofNullable(instant) + .map(Date::from) + .orElse(null); + } + + + public Balance toBalance(BitgetBalanceDto balance) { + return new Balance.Builder() + .currency(balance.getCurrency()) + .available(balance.getAvailable()) + .frozen(balance.getFrozen()) + .timestamp(toDate(balance.getTimestamp())) + .build(); + } + + + public Wallet toWallet(List bitgetBalanceDtos) { + List balances = bitgetBalanceDtos.stream() + .map(BitgetAdapters::toBalance) + .collect(Collectors.toList()); + + return Wallet.Builder.from(balances).id("spot").build(); + } + + } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAuthenticated.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAuthenticated.java new file mode 100644 index 00000000000..5aca871357a --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAuthenticated.java @@ -0,0 +1,31 @@ +package org.knowm.xchange.bitget; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import java.io.IOException; +import java.util.List; +import org.knowm.xchange.bitget.dto.BitgetException; +import org.knowm.xchange.bitget.dto.BitgetResponse; +import org.knowm.xchange.bitget.dto.account.BitgetBalanceDto; +import si.mazi.rescu.ParamsDigest; +import si.mazi.rescu.SynchronizedValueFactory; + +@Path("") +@Produces(MediaType.APPLICATION_JSON) +public interface BitgetAuthenticated { + + @GET + @Path("api/v2/spot/account/assets") + BitgetResponse> balances( + @HeaderParam("ACCESS-KEY") String apiKey, + @HeaderParam("ACCESS-SIGN") ParamsDigest signer, + @HeaderParam("ACCESS-PASSPHRASE") String passphrase, + @HeaderParam("ACCESS-TIMESTAMP") SynchronizedValueFactory timestamp) + throws IOException, BitgetException; + + + +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java index 8fcdae1d861..2ed614b44ab 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java @@ -7,6 +7,7 @@ import org.knowm.xchange.BaseExchange; import org.knowm.xchange.ExchangeSpecification; import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto; +import org.knowm.xchange.bitget.service.BitgetAccountService; import org.knowm.xchange.bitget.service.BitgetMarketDataService; import org.knowm.xchange.bitget.service.BitgetMarketDataServiceRaw; import org.knowm.xchange.dto.meta.ExchangeMetaData; @@ -17,7 +18,7 @@ public class BitgetExchange extends BaseExchange { @Override protected void initServices() { -// accountService = new BitgetAccountService(this); + accountService = new BitgetAccountService(this); marketDataService = new BitgetMarketDataService(this); // tradeService = new BitgetTradeService(this); } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/account/BitgetBalanceDto.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/account/BitgetBalanceDto.java new file mode 100755 index 00000000000..5e4813d3dd1 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/account/BitgetBalanceDto.java @@ -0,0 +1,37 @@ +package org.knowm.xchange.bitget.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.bitget.config.converter.StringToCurrencyConverter; +import org.knowm.xchange.currency.Currency; + +@Data +@Builder +@Jacksonized +public class BitgetBalanceDto { + + @JsonProperty("coin") + @JsonDeserialize(converter = StringToCurrencyConverter.class) + private Currency currency; + + @JsonProperty("available") + private BigDecimal available; + + @JsonProperty("frozen") + private BigDecimal frozen; + + @JsonProperty("locked") + private BigDecimal locked; + + @JsonProperty("limitAvailable") + private BigDecimal limitAvailable; + + @JsonProperty("uTime") + private Instant timestamp; + +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetTickerDto.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetTickerDto.java index d516ba1cb24..2fd05042b35 100755 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetTickerDto.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetTickerDto.java @@ -3,8 +3,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.math.BigDecimal; import java.time.Instant; -import java.util.Date; -import java.util.Optional; import lombok.Builder; import lombok.Data; import lombok.extern.jackson.Jacksonized; @@ -62,10 +60,4 @@ public class BitgetTickerDto { @JsonProperty("change24h") private BigDecimal change24h; - public Date getTimestampAsDate() { - return Optional.ofNullable(timestamp) - .map(Date::from) - .orElse(null); - } - } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetAccountService.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetAccountService.java new file mode 100644 index 00000000000..d8222013fd7 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetAccountService.java @@ -0,0 +1,33 @@ +package org.knowm.xchange.bitget.service; + +import java.io.IOException; +import java.util.List; +import org.knowm.xchange.bitget.BitgetAdapters; +import org.knowm.xchange.bitget.BitgetErrorAdapter; +import org.knowm.xchange.bitget.BitgetExchange; +import org.knowm.xchange.bitget.dto.BitgetException; +import org.knowm.xchange.bitget.dto.account.BitgetBalanceDto; +import org.knowm.xchange.dto.account.AccountInfo; +import org.knowm.xchange.dto.account.Wallet; +import org.knowm.xchange.service.account.AccountService; + +public class BitgetAccountService extends BitgetAccountServiceRaw implements AccountService { + + public BitgetAccountService(BitgetExchange exchange) { + super(exchange); + } + + + @Override + public AccountInfo getAccountInfo() throws IOException { + try { + List spotBalances = getBitgetBalances(); + Wallet wallet = BitgetAdapters.toWallet(spotBalances); + return new AccountInfo(wallet); + + } catch (BitgetException e) { + throw BitgetErrorAdapter.adapt(e); + } + } + +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetAccountServiceRaw.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetAccountServiceRaw.java new file mode 100644 index 00000000000..4ffc0195feb --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetAccountServiceRaw.java @@ -0,0 +1,18 @@ +package org.knowm.xchange.bitget.service; + +import java.io.IOException; +import java.util.List; +import org.knowm.xchange.bitget.BitgetExchange; +import org.knowm.xchange.bitget.dto.account.BitgetBalanceDto; + +public class BitgetAccountServiceRaw extends BitgetBaseService { + + public BitgetAccountServiceRaw(BitgetExchange exchange) { + super(exchange); + } + + + public List getBitgetBalances() throws IOException { + return bitgetAuthenticated.balances(apiKey, bitgetDigest, passphrase, exchange.getNonceFactory()).getData(); + } +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetBaseService.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetBaseService.java index 480d0e80a71..5d1f6bace4c 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetBaseService.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetBaseService.java @@ -1,6 +1,7 @@ package org.knowm.xchange.bitget.service; import org.knowm.xchange.bitget.Bitget; +import org.knowm.xchange.bitget.BitgetAuthenticated; import org.knowm.xchange.bitget.BitgetExchange; import org.knowm.xchange.bitget.config.BitgetJacksonObjectMapperFactory; import org.knowm.xchange.client.ExchangeRestProxyBuilder; @@ -10,7 +11,10 @@ public class BitgetBaseService extends BaseExchangeService implements BaseService { protected final String apiKey; + protected final String passphrase; protected final Bitget bitget; + protected final BitgetAuthenticated bitgetAuthenticated; + protected final BitgetDigest bitgetDigest; public BitgetBaseService(BitgetExchange exchange) { super(exchange); @@ -18,7 +22,14 @@ public BitgetBaseService(BitgetExchange exchange) { .forInterface(Bitget.class, exchange.getExchangeSpecification()) .clientConfigCustomizer(clientConfig -> clientConfig.setJacksonObjectMapperFactory(new BitgetJacksonObjectMapperFactory())) .build(); + bitgetAuthenticated = ExchangeRestProxyBuilder + .forInterface(BitgetAuthenticated.class, exchange.getExchangeSpecification()) + .clientConfigCustomizer(clientConfig -> clientConfig.setJacksonObjectMapperFactory(new BitgetJacksonObjectMapperFactory())) + .build(); + apiKey = exchange.getExchangeSpecification().getApiKey(); + passphrase = exchange.getExchangeSpecification().getPassword(); + bitgetDigest = BitgetDigest.createInstance(exchange.getExchangeSpecification().getSecretKey()); } } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetDigest.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetDigest.java new file mode 100644 index 00000000000..3d2a4ca24e0 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetDigest.java @@ -0,0 +1,45 @@ +package org.knowm.xchange.bitget.service; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Locale; +import javax.crypto.Mac; +import lombok.SneakyThrows; +import org.apache.commons.lang3.StringUtils; +import org.knowm.xchange.service.BaseParamsDigest; +import si.mazi.rescu.RestInvocation; + +public final class BitgetDigest extends BaseParamsDigest { + + private BitgetDigest(String secretKeyBase64) { + super(secretKeyBase64, HMAC_SHA_256); + } + + + public static BitgetDigest createInstance(String secretKeyBase64) { + return secretKeyBase64 == null ? null : new BitgetDigest(secretKeyBase64); + } + + + @SneakyThrows + @Override + public String digestParams(RestInvocation restInvocation) { + String method = restInvocation.getHttpMethod().toUpperCase(Locale.ROOT); + String path = restInvocation.getPath(); + + String query = StringUtils.defaultIfEmpty(restInvocation.getQueryString(), ""); + if (StringUtils.isNotEmpty(query)) { + query = "?" + query; + } + String body = StringUtils.defaultIfEmpty(restInvocation.getRequestBody(), ""); + + String timestamp = restInvocation.getHttpHeadersFromParams().get("ACCESS-TIMESTAMP"); + + String payloadToSign = String.format("%s%s/%s%s%s", timestamp, method, path, query, body); + + Mac mac = getMac(); + mac.update(payloadToSign.getBytes(StandardCharsets.UTF_8)); + + return Base64.getEncoder().encodeToString(mac.doFinal()); + } +} diff --git a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetAccountServiceIntegration.java b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetAccountServiceIntegration.java new file mode 100644 index 00000000000..637d669ef90 --- /dev/null +++ b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetAccountServiceIntegration.java @@ -0,0 +1,44 @@ +package org.knowm.xchange.bitget.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; + +import java.io.IOException; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.knowm.xchange.ExchangeFactory; +import org.knowm.xchange.ExchangeSpecification; +import org.knowm.xchange.bitget.BitgetExchange; +import org.knowm.xchange.dto.account.AccountInfo; + +class BitgetAccountServiceIntegration { + + static BitgetExchange exchange; + + + @BeforeAll + public static void credentialsPresent() { + // skip if there are no credentials + String apiKey = System.getProperty("apiKey"); + String secretKey = System.getProperty("secretKey"); + String passphrase = System.getProperty("passphrase"); + assumeThat(apiKey).isNotEmpty(); + assumeThat(secretKey).isNotEmpty(); + assumeThat(passphrase).isNotEmpty(); + + ExchangeSpecification exSpec = new ExchangeSpecification(BitgetExchange.class); + exSpec.setApiKey(apiKey); + exSpec.setSecretKey(secretKey); + exSpec.setPassword(passphrase); + exchange = (BitgetExchange) ExchangeFactory.INSTANCE.createExchange(exSpec); + } + + + @Test + void valid_balances() throws IOException { + AccountInfo accountInfo = exchange.getAccountService().getAccountInfo(); + assertThat(accountInfo.getWallet("spot").getBalances()).isNotEmpty(); + } + + +} \ No newline at end of file diff --git a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetDigestTest.java b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetDigestTest.java new file mode 100644 index 00000000000..75fbd049f44 --- /dev/null +++ b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetDigestTest.java @@ -0,0 +1,40 @@ +package org.knowm.xchange.bitget.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 BitgetDigestTest { + + @Mock + RestInvocation restInvocation; + + + @Test + void signature_no_query_params() { + BitgetDigest bitgetDigest = BitgetDigest.createInstance("secret"); + + when(restInvocation.getHttpMethod()).thenReturn("get"); + when(restInvocation.getPath()).thenReturn("api/v2/spot/account/assets"); + when(restInvocation.getQueryString()).thenReturn(""); + when(restInvocation.getRequestBody()).thenReturn(null); + Map headers = new HashMap<>(); + headers.put("ACCESS-TIMESTAMP", "1725040472073"); + when(restInvocation.getHttpHeadersFromParams()).thenReturn(headers); + + String actual = bitgetDigest.digestParams(restInvocation); + String expected = "p8vWylxL4JJCyy6B81n82ZEySiWChClCw99FTDocviA="; + + assertThat(actual).isEqualTo(expected); + } + + +} \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/rest/sign.js b/xchange-bitget/src/test/resources/rest/sign.js new file mode 100644 index 00000000000..2daee399999 --- /dev/null +++ b/xchange-bitget/src/test/resources/rest/sign.js @@ -0,0 +1,22 @@ +export function gen_sign(method, request) { + const pattern = RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"); + const url = request.url.tryGetSubstituted(); + const matches = url.match(pattern); + + const path = matches[5]; + + let query = matches[7] || ""; + if (query !== "") { + query = "?" + query; + } + + const body = request.body.tryGetSubstituted() || ""; + const timestamp = Math.floor(Date.now()).toFixed(); + + const payloadToSign = `${timestamp}${method}${path}${query}${body}`; + const apiSecret = request.environment.get("api_secret"); + const sign = crypto.hmac.sha256().withTextSecret(apiSecret).updateWithText(payloadToSign).digest().toBase64(); + + request.variables.set("timestamp", timestamp); + request.variables.set("sign", sign); +} \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/rest/spot.http b/xchange-bitget/src/test/resources/rest/spot.http index dd85d32d49d..2189955306c 100644 --- a/xchange-bitget/src/test/resources/rest/spot.http +++ b/xchange-bitget/src/test/resources/rest/spot.http @@ -10,3 +10,27 @@ GET {{api_host}}/api/v2/spot/public/symbols GET {{api_host}}/api/v2/spot/market/tickers +### Get Account Information +< {% + import {gen_sign} from 'sign.js' + gen_sign("GET", request); +%} +GET {{api_host}}/api/v2/spot/account/info +ACCESS-KEY: {{api_key}} +ACCESS-SIGN: {{sign}} +ACCESS-TIMESTAMP: {{timestamp}} +ACCESS-PASSPHRASE: {{api_passphrase}} + + +### Get Account assets +< {% + import {gen_sign} from 'sign.js' + gen_sign("GET", request); +%} +GET {{api_host}}/api/v2/spot/account/assets +ACCESS-KEY: {{api_key}} +ACCESS-SIGN: {{sign}} +ACCESS-TIMESTAMP: {{timestamp}} +ACCESS-PASSPHRASE: {{api_passphrase}} + + From a0119293664123fbc96f59d6b97b6f6cd3b4ff1f Mon Sep 17 00:00:00 2001 From: Dmitri Karpovich Date: Fri, 30 Aug 2024 22:09:53 +0200 Subject: [PATCH 5/8] [bitget] Add parameter to coin infos --- .../java/org/knowm/xchange/bitget/Bitget.java | 2 +- .../knowm/xchange/bitget/BitgetAdapters.java | 7 +++++++ .../service/BitgetMarketDataServiceRaw.java | 5 +++-- .../BitgetMarketDataServiceRawIntegration.java | 18 +++++++++++++++++- .../src/test/resources/rest/spot.http | 2 +- 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java index 8ed381d755f..5c1df5ee6a9 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java @@ -25,7 +25,7 @@ public interface Bitget { @GET @Path("api/v2/spot/public/coins") - BitgetResponse> coins() throws IOException, BitgetException; + BitgetResponse> coins(@QueryParam("coin") String coin) throws IOException, BitgetException; @GET diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java index 2a24178451b..e25b52a54db 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java @@ -36,6 +36,13 @@ public String toString(Instrument instrument) { } + public String toString(Currency currency) { + return Optional.ofNullable(currency) + .map(Currency::getCurrencyCode) + .orElse(null); + } + + public void putSymbolMapping(String symbol, CurrencyPair currencyPair) { SYMBOL_TO_CURRENCY_PAIR.put(symbol, currencyPair); } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java index 1e3fe76c977..d5c5d7a1627 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java @@ -8,6 +8,7 @@ import org.knowm.xchange.bitget.dto.marketdata.BitgetServerTime; import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto; import org.knowm.xchange.bitget.dto.marketdata.BitgetTickerDto; +import org.knowm.xchange.currency.Currency; import org.knowm.xchange.instrument.Instrument; public class BitgetMarketDataServiceRaw extends BitgetBaseService { @@ -23,8 +24,8 @@ public BitgetServerTime getBitgetServerTime() throws IOException { } - public List getBitgetCoinDtoList() throws IOException { - return bitget.coins().getData(); + public List getBitgetCoinDtoList(Currency currency) throws IOException { + return bitget.coins(BitgetAdapters.toString(currency)).getData(); } diff --git a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRawIntegration.java b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRawIntegration.java index 4a8782e1b91..ee24c10a9be 100644 --- a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRawIntegration.java +++ b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRawIntegration.java @@ -9,6 +9,7 @@ import org.knowm.xchange.bitget.BitgetIntegrationTestParent; import org.knowm.xchange.bitget.dto.marketdata.BitgetCoinDto; import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto; +import org.knowm.xchange.currency.Currency; class BitgetMarketDataServiceRawIntegration extends BitgetIntegrationTestParent { @@ -16,7 +17,7 @@ class BitgetMarketDataServiceRawIntegration extends BitgetIntegrationTestParent @Test void valid_coins() throws IOException { - List coins = bitgetMarketDataServiceRaw.getBitgetCoinDtoList(); + List coins = bitgetMarketDataServiceRaw.getBitgetCoinDtoList(null); assertThat(coins).isNotEmpty(); @@ -34,6 +35,21 @@ void valid_coins() throws IOException { } + @Test + void valid_coin() throws IOException { + List coins = bitgetMarketDataServiceRaw.getBitgetCoinDtoList(Currency.USDT); + + assertThat(coins).hasSize(1); + + assertThat(coins.get(0).getCurrency()).isEqualTo(Currency.USDT); + assertThat(coins.get(0).getCoinId()).isNotNull(); + assertThat(coins.get(0).getChains()).allSatisfy(chain -> { + assertThat(chain.getChain()).isNotNull(); + }); + + } + + @Test void valid_symbol() throws IOException { List symbols = bitgetMarketDataServiceRaw.getBitgetSymbolDtos(BTC_USDT); diff --git a/xchange-bitget/src/test/resources/rest/spot.http b/xchange-bitget/src/test/resources/rest/spot.http index 2189955306c..407d8648f18 100644 --- a/xchange-bitget/src/test/resources/rest/spot.http +++ b/xchange-bitget/src/test/resources/rest/spot.http @@ -1,5 +1,5 @@ ### Get Coin Info -GET {{api_host}}/api/v2/spot/public/coins +GET {{api_host}}/api/v2/spot/public/coins?coin=usdt ### Get Symbol Info From 6553bb699823e732f581f7b229dc179bceb6f925 Mon Sep 17 00:00:00 2001 From: Dmitri Karpovich Date: Fri, 30 Aug 2024 23:18:01 +0200 Subject: [PATCH 6/8] [bitget] Add getting of orderbook --- .../java/org/knowm/xchange/bitget/Bitget.java | 7 ++++ .../knowm/xchange/bitget/BitgetAdapters.java | 33 +++++++++++++++++ .../dto/marketdata/BitgetMarketDepthDto.java | 36 +++++++++++++++++++ .../service/BitgetMarketDataService.java | 19 ++++++++++ .../service/BitgetMarketDataServiceRaw.java | 6 ++++ .../BitgetMarketDataServiceIntegration.java | 23 ++++++++++++ .../src/test/resources/rest/spot.http | 4 +++ 7 files changed, 128 insertions(+) create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetMarketDepthDto.java diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java index 5c1df5ee6a9..2ccacc26ccb 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/Bitget.java @@ -10,6 +10,7 @@ import org.knowm.xchange.bitget.dto.BitgetException; import org.knowm.xchange.bitget.dto.BitgetResponse; import org.knowm.xchange.bitget.dto.marketdata.BitgetCoinDto; +import org.knowm.xchange.bitget.dto.marketdata.BitgetMarketDepthDto; import org.knowm.xchange.bitget.dto.marketdata.BitgetServerTime; import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto; import org.knowm.xchange.bitget.dto.marketdata.BitgetTickerDto; @@ -40,4 +41,10 @@ BitgetResponse> tickers(@QueryParam("symbol") String symbo throws IOException, BitgetException; + @GET + @Path("api/v2/spot/market/orderbook") + BitgetResponse orderbook(@QueryParam("symbol") String symbol) + throws IOException, BitgetException; + + } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java index e25b52a54db..b0f18196572 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java @@ -9,15 +9,19 @@ import java.util.stream.Collectors; import lombok.experimental.UtilityClass; import org.knowm.xchange.bitget.dto.account.BitgetBalanceDto; +import org.knowm.xchange.bitget.dto.marketdata.BitgetMarketDepthDto; import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto; import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto.Status; import org.knowm.xchange.bitget.dto.marketdata.BitgetTickerDto; import org.knowm.xchange.currency.Currency; import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order.OrderType; 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.meta.InstrumentMetaData; +import org.knowm.xchange.dto.trade.LimitOrder; import org.knowm.xchange.instrument.Instrument; @UtilityClass @@ -115,4 +119,33 @@ public Wallet toWallet(List bitgetBalanceDtos) { } + public OrderBook toOrderBook(BitgetMarketDepthDto bitgetMarketDepthDto, Instrument instrument) { + List asks = bitgetMarketDepthDto.getAsks().stream() + .map(priceSizeEntry -> + new LimitOrder( + OrderType.ASK, + priceSizeEntry.getSize(), + instrument, + null, + null, + priceSizeEntry.getPrice()) + ) + .collect(Collectors.toList()); + + List bids = bitgetMarketDepthDto.getBids().stream() + .map(priceSizeEntry -> + new LimitOrder( + OrderType.BID, + priceSizeEntry.getSize(), + instrument, + null, + null, + priceSizeEntry.getPrice()) + ) + .collect(Collectors.toList()); + + return new OrderBook(toDate(bitgetMarketDepthDto.getTimestamp()), asks, bids); + } + + } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetMarketDepthDto.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetMarketDepthDto.java new file mode 100644 index 00000000000..17746d1e792 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/marketdata/BitgetMarketDepthDto.java @@ -0,0 +1,36 @@ +package org.knowm.xchange.bitget.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 BitgetMarketDepthDto { + + @JsonProperty("asks") + List asks; + + @JsonProperty("bids") + List bids; + + @JsonProperty("ts") + Instant timestamp; + + + @Data + @Builder + @Jacksonized + @JsonFormat(shape = JsonFormat.Shape.ARRAY) + public static class PriceSizeEntry { + BigDecimal price; + + BigDecimal size; + } +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataService.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataService.java index 07b27338738..f02332cdf95 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataService.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataService.java @@ -13,6 +13,7 @@ import org.knowm.xchange.bitget.dto.BitgetException; import org.knowm.xchange.bitget.dto.marketdata.BitgetTickerDto; 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.ExchangeHealth; import org.knowm.xchange.instrument.Instrument; @@ -74,4 +75,22 @@ public List getTickers(Params params) throws IOException { throw BitgetErrorAdapter.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 { + Objects.requireNonNull(instrument); + + try { + return BitgetAdapters.toOrderBook(getBitgetMarketDepthDtos(instrument), instrument); + } catch (BitgetException e) { + throw BitgetErrorAdapter.adapt(e); + } + } } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java index d5c5d7a1627..ee8882c9410 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceRaw.java @@ -5,6 +5,7 @@ import org.knowm.xchange.bitget.BitgetAdapters; import org.knowm.xchange.bitget.BitgetExchange; import org.knowm.xchange.bitget.dto.marketdata.BitgetCoinDto; +import org.knowm.xchange.bitget.dto.marketdata.BitgetMarketDepthDto; import org.knowm.xchange.bitget.dto.marketdata.BitgetServerTime; import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto; import org.knowm.xchange.bitget.dto.marketdata.BitgetTickerDto; @@ -39,4 +40,9 @@ public List getBitgetTickerDtos(Instrument instrument) throws I } + public BitgetMarketDepthDto getBitgetMarketDepthDtos(Instrument instrument) throws IOException { + return bitget.orderbook(BitgetAdapters.toString(instrument)).getData(); + } + + } diff --git a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceIntegration.java b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceIntegration.java index 1e670e45345..0a0d09f0bf8 100644 --- a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceIntegration.java +++ b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetMarketDataServiceIntegration.java @@ -8,6 +8,8 @@ import org.junit.jupiter.api.Test; import org.knowm.xchange.bitget.BitgetIntegrationTestParent; 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.exceptions.InstrumentNotValidException; @@ -52,4 +54,25 @@ void valid_tickers() throws IOException { } + @Test + void valid_orderbook() throws IOException { + OrderBook orderBook = exchange.getMarketDataService().getOrderBook(CurrencyPair.BTC_USDT); + + assertThat(orderBook.getBids()).isNotEmpty(); + assertThat(orderBook.getAsks()).isNotEmpty(); + + assertThat(orderBook.getAsks().get(0).getLimitPrice()).isGreaterThan(orderBook.getBids().get(0).getLimitPrice()); + + assertThat(orderBook.getBids()).allSatisfy(limitOrder -> { + assertThat(limitOrder.getInstrument()).isEqualTo(CurrencyPair.BTC_USDT); + assertThat(limitOrder.getType()).isEqualTo(OrderType.BID); + }); + + assertThat(orderBook.getAsks()).allSatisfy(limitOrder -> { + assertThat(limitOrder.getInstrument()).isEqualTo(CurrencyPair.BTC_USDT); + assertThat(limitOrder.getType()).isEqualTo(OrderType.ASK); + }); + } + + } \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/rest/spot.http b/xchange-bitget/src/test/resources/rest/spot.http index 407d8648f18..c5c1e333b2d 100644 --- a/xchange-bitget/src/test/resources/rest/spot.http +++ b/xchange-bitget/src/test/resources/rest/spot.http @@ -2,6 +2,10 @@ GET {{api_host}}/api/v2/spot/public/coins?coin=usdt +### Get OrderBook Depth +GET {{api_host}}/api/v2/spot/market/orderbook?symbol=btcusdt + + ### Get Symbol Info GET {{api_host}}/api/v2/spot/public/symbols From 447a67257928790a0566a99c14238a421c586129 Mon Sep 17 00:00:00 2001 From: Dmitri Karpovich Date: Sun, 1 Sep 2024 14:46:00 +0200 Subject: [PATCH 7/8] [bitget] Add getting of orders --- xchange-bitget/pom.xml | 6 + .../knowm/xchange/bitget/BitgetAdapters.java | 71 +++++++++ .../xchange/bitget/BitgetAuthenticated.java | 12 ++ .../xchange/bitget/BitgetErrorAdapter.java | 9 ++ .../knowm/xchange/bitget/BitgetExchange.java | 3 +- .../BitgetJacksonObjectMapperFactory.java | 3 + .../converter/StringToOrderTypeConverter.java | 21 +++ .../deserializer/FeeDetailDeserializer.java | 19 +++ .../bitget/dto/trade/BitgetOrderInfoDto.java | 147 ++++++++++++++++++ .../bitget/service/BitgetTradeService.java | 39 +++++ .../bitget/service/BitgetTradeServiceRaw.java | 24 +++ .../bitget/BitgetExchangeWiremock.java | 53 +++++++ .../service/BitgetTradeServiceTest.java | 48 ++++++ .../__files/api_v2_spot_public_symbols.json | 58 +++++++ ...e_orderinfo-t-valid-market-sell-order.json | 30 ++++ .../mappings/api_v2_spot_public_symbols.json | 15 ++ ...e_orderinfo-t-valid-market-sell-order.json | 15 ++ .../src/test/resources/rest/spot.http | 54 +++++++ 18 files changed, 626 insertions(+), 1 deletion(-) create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/converter/StringToOrderTypeConverter.java create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/deserializer/FeeDetailDeserializer.java create mode 100755 xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/trade/BitgetOrderInfoDto.java create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetTradeService.java create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetTradeServiceRaw.java create mode 100644 xchange-bitget/src/test/java/org/knowm/xchange/bitget/BitgetExchangeWiremock.java create mode 100644 xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetTradeServiceTest.java create mode 100644 xchange-bitget/src/test/resources/__files/api_v2_spot_public_symbols.json create mode 100644 xchange-bitget/src/test/resources/__files/api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json create mode 100644 xchange-bitget/src/test/resources/mappings/api_v2_spot_public_symbols.json create mode 100644 xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json diff --git a/xchange-bitget/pom.xml b/xchange-bitget/pom.xml index 2b9a8eec7f1..ab88f4ced96 100644 --- a/xchange-bitget/pom.xml +++ b/xchange-bitget/pom.xml @@ -46,6 +46,12 @@ test + + org.wiremock + wiremock + test + + diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java index b0f18196572..1a72a2bd870 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java @@ -1,10 +1,12 @@ package org.knowm.xchange.bitget; +import java.math.BigDecimal; import java.time.Instant; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import lombok.experimental.UtilityClass; @@ -13,8 +15,12 @@ import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto; import org.knowm.xchange.bitget.dto.marketdata.BitgetSymbolDto.Status; import org.knowm.xchange.bitget.dto.marketdata.BitgetTickerDto; +import org.knowm.xchange.bitget.dto.trade.BitgetOrderInfoDto; +import org.knowm.xchange.bitget.dto.trade.BitgetOrderInfoDto.BitgetOrderStatus; 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.account.Balance; import org.knowm.xchange.dto.account.Wallet; @@ -22,6 +28,7 @@ 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.instrument.Instrument; @UtilityClass @@ -148,4 +155,68 @@ public OrderBook toOrderBook(BitgetMarketDepthDto bitgetMarketDepthDto, Instrume } + public Order toOrder(BitgetOrderInfoDto order) { + if (order == null) { + return null; + } + + Instrument instrument = toCurrencyPair(order.getSymbol()); + Objects.requireNonNull(instrument); + OrderType orderType = order.getOrderSide(); + + Order.Builder builder; + switch (order.getOrderType()) { + case MARKET: + builder = new MarketOrder.Builder(orderType, instrument); + break; + case LIMIT: + builder = new LimitOrder.Builder(orderType, instrument) + .limitPrice(order.getPrice()); + break; + default: + throw new IllegalArgumentException("Can't map " + order.getOrderType()); + } + + if (orderType == OrderType.BID) { + // buy orders fill quote + builder.cumulativeAmount(order.getQuoteVolume()); + } else if (orderType == OrderType.ASK) { + // sell orders fill asset + builder.cumulativeAmount(order.getBaseVolume()); + } else { + throw new IllegalArgumentException("Can't map " + orderType); + } + + BigDecimal fee = order.getFee(); + if (fee != null) { + builder.fee(fee); + } + + return builder + .id(String.valueOf(order.getOrderId())) + .averagePrice(order.getPriceAvg()) + .originalAmount(order.getSize()) + .userReference(order.getClientOid()) + .timestamp(toDate(order.getCreatedAt())) + .orderStatus(toOrderStatus(order.getOrderStatus())) + .build(); + } + + + public OrderStatus toOrderStatus(BitgetOrderStatus bitgetOrderStatus) { + switch (bitgetOrderStatus) { + case PENDING: + return OrderStatus.NEW; + case PARTIALLY_FILLED: + return OrderStatus.PARTIALLY_FILLED; + case FILLED: + return OrderStatus.FILLED; + case CANCELLED: + return OrderStatus.CANCELED; + default: + throw new IllegalArgumentException("Can't map " + bitgetOrderStatus); + } + } + + } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAuthenticated.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAuthenticated.java index 5aca871357a..d9368782ecd 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAuthenticated.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAuthenticated.java @@ -4,12 +4,14 @@ import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.Path; 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.bitget.dto.BitgetException; import org.knowm.xchange.bitget.dto.BitgetResponse; import org.knowm.xchange.bitget.dto.account.BitgetBalanceDto; +import org.knowm.xchange.bitget.dto.trade.BitgetOrderInfoDto; import si.mazi.rescu.ParamsDigest; import si.mazi.rescu.SynchronizedValueFactory; @@ -27,5 +29,15 @@ BitgetResponse> balances( throws IOException, BitgetException; + @GET + @Path("api/v2/spot/trade/orderInfo") + BitgetResponse> orderInfo( + @HeaderParam("ACCESS-KEY") String apiKey, + @HeaderParam("ACCESS-SIGN") ParamsDigest signer, + @HeaderParam("ACCESS-PASSPHRASE") String passphrase, + @HeaderParam("ACCESS-TIMESTAMP") SynchronizedValueFactory timestamp, + @QueryParam("orderId") String orderId) + throws IOException, BitgetException; + } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetErrorAdapter.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetErrorAdapter.java index ca5386d4c89..34755fab16c 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetErrorAdapter.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetErrorAdapter.java @@ -4,11 +4,15 @@ import org.knowm.xchange.bitget.dto.BitgetException; import org.knowm.xchange.exceptions.ExchangeException; import org.knowm.xchange.exceptions.InstrumentNotValidException; +import org.knowm.xchange.exceptions.OrderAmountUnderMinimumException; @UtilityClass public class BitgetErrorAdapter { public final int INVALID_PARAMETER_CODE = 40034; + public final int MIN_ORDER_SIZE = 13008; + public final int MIN_ORDER_AMOUNT = 45110; + public final int MIN_ORDER_QTY = 45111; public ExchangeException adapt(BitgetException e) { @@ -16,6 +20,11 @@ public ExchangeException adapt(BitgetException e) { case INVALID_PARAMETER_CODE: return new InstrumentNotValidException(e.getMessage(), e); + case MIN_ORDER_SIZE: + case MIN_ORDER_AMOUNT: + case MIN_ORDER_QTY: + return new OrderAmountUnderMinimumException(e.getMessage(), e); + default: return new ExchangeException(e.getMessage(), e); } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java index 2ed614b44ab..b7f3ce80acb 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetExchange.java @@ -10,6 +10,7 @@ import org.knowm.xchange.bitget.service.BitgetAccountService; import org.knowm.xchange.bitget.service.BitgetMarketDataService; import org.knowm.xchange.bitget.service.BitgetMarketDataServiceRaw; +import org.knowm.xchange.bitget.service.BitgetTradeService; import org.knowm.xchange.dto.meta.ExchangeMetaData; import org.knowm.xchange.dto.meta.InstrumentMetaData; import org.knowm.xchange.instrument.Instrument; @@ -20,7 +21,7 @@ public class BitgetExchange extends BaseExchange { protected void initServices() { accountService = new BitgetAccountService(this); marketDataService = new BitgetMarketDataService(this); -// tradeService = new BitgetTradeService(this); + tradeService = new BitgetTradeService(this); } @Override diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/BitgetJacksonObjectMapperFactory.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/BitgetJacksonObjectMapperFactory.java index 51d95ab13d9..350cf8e2b48 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/BitgetJacksonObjectMapperFactory.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/BitgetJacksonObjectMapperFactory.java @@ -18,6 +18,9 @@ public void configureObjectMapper(ObjectMapper objectMapper) { // don't write nulls objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + // don't fail un unknown properties + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + // enable parsing to Instant objectMapper.registerModule(new JavaTimeModule()); } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/converter/StringToOrderTypeConverter.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/converter/StringToOrderTypeConverter.java new file mode 100644 index 00000000000..0d747baa28e --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/converter/StringToOrderTypeConverter.java @@ -0,0 +1,21 @@ +package org.knowm.xchange.bitget.config.converter; + +import com.fasterxml.jackson.databind.util.StdConverter; +import java.util.Locale; +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.toUpperCase(Locale.ROOT)) { + case "BUY": + return OrderType.BID; + case "SELL": + return OrderType.ASK; + default: + throw new IllegalArgumentException("Can't map " + value); + } + } +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/deserializer/FeeDetailDeserializer.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/deserializer/FeeDetailDeserializer.java new file mode 100644 index 00000000000..4628bd65e43 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/deserializer/FeeDetailDeserializer.java @@ -0,0 +1,19 @@ +package org.knowm.xchange.bitget.config.deserializer; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import org.knowm.xchange.bitget.dto.trade.BitgetOrderInfoDto.FeeDetail; + +public class FeeDetailDeserializer extends JsonDeserializer { + + @Override + public FeeDetail deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String text = p.getText(); + ObjectMapper objectMapper = (ObjectMapper) p.getCodec(); + + return objectMapper.readValue(text, FeeDetail.class); + } +} \ No newline at end of file diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/trade/BitgetOrderInfoDto.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/trade/BitgetOrderInfoDto.java new file mode 100755 index 00000000000..3a6c9e2dc9f --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/trade/BitgetOrderInfoDto.java @@ -0,0 +1,147 @@ +package org.knowm.xchange.bitget.dto.trade; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.Optional; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.bitget.config.converter.StringToOrderTypeConverter; +import org.knowm.xchange.bitget.config.deserializer.FeeDetailDeserializer; +import org.knowm.xchange.dto.Order; + +@Data +@Builder +@Jacksonized +public class BitgetOrderInfoDto { + + @JsonProperty("userId") + private String acccountId; + + @JsonProperty("symbol") + private String symbol; + + @JsonProperty("orderId") + private String orderId; + + @JsonProperty("clientOid") + private String clientOid; + + @JsonProperty("price") + private BigDecimal price; + + @JsonProperty("size") + private BigDecimal size; + + @JsonProperty("orderType") + private OrderType orderType; + + @JsonProperty("side") + @JsonDeserialize(converter = StringToOrderTypeConverter.class) + private Order.OrderType orderSide; + + @JsonProperty("status") + private BitgetOrderStatus orderStatus; + + @JsonProperty("priceAvg") + private BigDecimal priceAvg; + + @JsonProperty("baseVolume") + private BigDecimal baseVolume; + + @JsonProperty("quoteVolume") + private BigDecimal quoteVolume; + + @JsonProperty("enterPointSource") + private String enterPointSource; + + @JsonProperty("cTime") + private Instant createdAt; + + @JsonProperty("uTime") + private Instant updatedAt; + + @JsonProperty("orderSource") + private OrderSource orderSource; + + @JsonProperty("feeDetail") + @JsonDeserialize(using = FeeDetailDeserializer.class) + private FeeDetail feeDetail; + + + public BigDecimal getFee() { + return Optional.ofNullable(feeDetail) + .map(FeeDetail::getNewFees) + .map(NewFees::getTotalFee) + .map(BigDecimal::abs) + .orElse(null); + } + + + public static enum OrderType { + @JsonProperty("limit") + LIMIT, + + @JsonProperty("market") + MARKET + } + + + public static enum BitgetOrderStatus { + @JsonProperty("live") + PENDING, + + @JsonProperty("partially_filled") + PARTIALLY_FILLED, + + @JsonProperty("filled") + FILLED, + + @JsonProperty("cancelled") + CANCELLED + + } + + + public static enum OrderSource { + @JsonProperty("normal") + NORMAL, + + @JsonProperty("market") + MARKET, + + @JsonProperty("spot_trader_buy") + SPOT_TRADER_BUY, + + @JsonProperty("spot_follower_buy") + SPOT_FOLLOWER_BUY, + + @JsonProperty("spot_trader_sell") + SPOT_TRADER_SELL, + + @JsonProperty("spot_follower_sell") + SPOT_FOLLOWER_SELL + + } + + + @Data + @Builder + @Jacksonized + public static class NewFees { + @JsonProperty("t") + private BigDecimal totalFee; + } + + + @Data + @Builder + @Jacksonized + public static class FeeDetail { + @JsonProperty("newFees") + private NewFees newFees; + } + +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetTradeService.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetTradeService.java new file mode 100644 index 00000000000..1b4f0933ce7 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetTradeService.java @@ -0,0 +1,39 @@ +package org.knowm.xchange.bitget.service; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import org.apache.commons.lang3.Validate; +import org.knowm.xchange.bitget.BitgetAdapters; +import org.knowm.xchange.bitget.BitgetErrorAdapter; +import org.knowm.xchange.bitget.BitgetExchange; +import org.knowm.xchange.bitget.dto.BitgetException; +import org.knowm.xchange.bitget.dto.trade.BitgetOrderInfoDto; +import org.knowm.xchange.dto.Order; +import org.knowm.xchange.service.trade.TradeService; +import org.knowm.xchange.service.trade.params.orders.DefaultQueryOrderParam; +import org.knowm.xchange.service.trade.params.orders.OrderQueryParams; + +public class BitgetTradeService extends BitgetTradeServiceRaw implements TradeService { + + public BitgetTradeService(BitgetExchange exchange) { + super(exchange); + } + + + @Override + public Collection getOrder(OrderQueryParams... orderQueryParams) throws IOException { + Validate.validState(orderQueryParams.length == 1); + Validate.isInstanceOf(DefaultQueryOrderParam.class, orderQueryParams[0]); + DefaultQueryOrderParam params = (DefaultQueryOrderParam) orderQueryParams[0]; + + try { + BitgetOrderInfoDto orderStatus = bitgetOrderInfoDto(params.getOrderId()); + return Collections.singletonList(BitgetAdapters.toOrder(orderStatus)); + } + catch (BitgetException e) { + throw BitgetErrorAdapter.adapt(e); + } + + } +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetTradeServiceRaw.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetTradeServiceRaw.java new file mode 100644 index 00000000000..f859f2fb011 --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetTradeServiceRaw.java @@ -0,0 +1,24 @@ +package org.knowm.xchange.bitget.service; + +import java.io.IOException; +import java.util.List; +import org.knowm.xchange.bitget.BitgetExchange; +import org.knowm.xchange.bitget.dto.trade.BitgetOrderInfoDto; + +public class BitgetTradeServiceRaw extends BitgetBaseService { + + public BitgetTradeServiceRaw(BitgetExchange exchange) { + super(exchange); + } + + + public BitgetOrderInfoDto bitgetOrderInfoDto(String orderId) throws IOException { + List results = bitgetAuthenticated.orderInfo( + apiKey, bitgetDigest, passphrase, exchange.getNonceFactory(), orderId).getData(); + if (results.size() != 1) { + return null; + } + return results.get(0); + } + +} diff --git a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/BitgetExchangeWiremock.java b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/BitgetExchangeWiremock.java new file mode 100644 index 00000000000..446f4e6a930 --- /dev/null +++ b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/BitgetExchangeWiremock.java @@ -0,0 +1,53 @@ +package org.knowm.xchange.bitget; + +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 BitgetExchangeWiremock { + + protected static BitgetExchange 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(BitgetExchange.class); + exSpec.setSslUri("http://localhost:" + wireMockServer.port()); + exSpec.setApiKey("a"); + exSpec.setSecretKey("b"); + exSpec.setPassword("c"); + + if (IS_RECORDING) { + // use default url and record the requests + wireMockServer.startRecording( + new RecordSpecBuilder() + .forTarget("https://api.bitget.com") + .matchRequestBodyWithEqualToJson() + .extractTextBodiesOver(1L) + .chooseBodyMatchTypeAutomatically()); + } + + exchange = (BitgetExchange) ExchangeFactory.INSTANCE.createExchange(exSpec); + } + + @AfterAll + public static void stop() { + if (IS_RECORDING) { + wireMockServer.stopRecording(); + } + wireMockServer.stop(); + } +} diff --git a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetTradeServiceTest.java b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetTradeServiceTest.java new file mode 100644 index 00000000000..e6f70a4f078 --- /dev/null +++ b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetTradeServiceTest.java @@ -0,0 +1,48 @@ +package org.knowm.xchange.bitget.service; + +import static org.assertj.core.api.Assertions.assertThat; + +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.bitget.BitgetExchangeWiremock; +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.MarketOrder; +import org.knowm.xchange.service.trade.TradeService; + +class BitgetTradeServiceTest extends BitgetExchangeWiremock { + + TradeService tradeService = exchange.getTradeService(); + + + @Test + void sell_order_details() throws IOException { + MarketOrder expected = + new MarketOrder.Builder(OrderType.ASK, new CurrencyPair("GOMINING/USDT")) + .id("1213530920130613257") + .userReference("t-valid-market-sell-order") + .timestamp(Date.from(Instant.parse("2024-08-30T21:43:58.350Z"))) + .originalAmount(new BigDecimal("3")) + .orderStatus(OrderStatus.FILLED) + .cumulativeAmount(new BigDecimal("3")) + .averagePrice(new BigDecimal("0.37846")) + .fee(new BigDecimal("0.00113538")) + .build(); + + Collection orders = tradeService.getOrder("1213530920130613257"); + assertThat(orders).hasSize(1); + assertThat(orders).first() + .usingComparatorForType(BigDecimal::compareTo, BigDecimal.class) + .usingRecursiveComparison().isEqualTo(expected); + } + + + + +} \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/__files/api_v2_spot_public_symbols.json b/xchange-bitget/src/test/resources/__files/api_v2_spot_public_symbols.json new file mode 100644 index 00000000000..2ad8c766358 --- /dev/null +++ b/xchange-bitget/src/test/resources/__files/api_v2_spot_public_symbols.json @@ -0,0 +1,58 @@ +{ + "code": "00000", + "msg": "success", + "requestTime": 1725192127389, + "data": [ + { + "symbol": "BTCUSDT", + "baseCoin": "BTC", + "quoteCoin": "USDT", + "minTradeAmount": "0", + "maxTradeAmount": "10000000000", + "takerFeeRate": "0.002", + "makerFeeRate": "0.002", + "pricePrecision": "2", + "quantityPrecision": "6", + "quotePrecision": "6", + "status": "online", + "minTradeUSDT": "1", + "buyLimitPriceRatio": "0.05", + "sellLimitPriceRatio": "0.05", + "areaSymbol": "no" + }, + { + "symbol": "ETHUSDT", + "baseCoin": "ETH", + "quoteCoin": "USDT", + "minTradeAmount": "0", + "maxTradeAmount": "10000000000", + "takerFeeRate": "0.002", + "makerFeeRate": "0.002", + "pricePrecision": "2", + "quantityPrecision": "4", + "quotePrecision": "6", + "status": "online", + "minTradeUSDT": "1", + "buyLimitPriceRatio": "0.05", + "sellLimitPriceRatio": "0.05", + "areaSymbol": "no" + }, + { + "symbol": "GOMININGUSDT", + "baseCoin": "GOMINING", + "quoteCoin": "USDT", + "minTradeAmount": "0", + "maxTradeAmount": "10000000000", + "takerFeeRate": "0.001", + "makerFeeRate": "0.001", + "pricePrecision": "5", + "quantityPrecision": "2", + "quotePrecision": "6", + "status": "online", + "minTradeUSDT": "1", + "buyLimitPriceRatio": "10", + "sellLimitPriceRatio": "0.9", + "areaSymbol": "no" + } + ] +} \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/__files/api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json b/xchange-bitget/src/test/resources/__files/api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json new file mode 100644 index 00000000000..733b02ca45a --- /dev/null +++ b/xchange-bitget/src/test/resources/__files/api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json @@ -0,0 +1,30 @@ +{ + "code": "00000", + "msg": "success", + "requestTime": 1725192128082, + "data": [ + { + "userId": "1548914322", + "symbol": "GOMININGUSDT", + "orderId": "1213530920130613257", + "clientOid": "t-valid-market-sell-order", + "price": "0", + "size": "3.0000000000000000", + "orderType": "market", + "side": "sell", + "status": "filled", + "priceAvg": "0.3784600000000000", + "baseVolume": "3.0000000000000000", + "quoteVolume": "1.1353800000000000", + "enterPointSource": "API", + "feeDetail": "{\"newFees\":{\"c\":0,\"d\":0,\"deduction\":false,\"r\":-0.00113538,\"t\":-0.00113538,\"totalDeductionFee\":0},\"USDT\":{\"deduction\":false,\"feeCoinCode\":\"USDT\",\"totalDeductionFee\":0,\"totalFee\":-0.0011353800000000}}", + "orderSource": "market", + "tpslType": "normal", + "triggerPrice": null, + "quoteCoin": "USDT", + "baseCoin": "GOMINING", + "cTime": "1725054238350", + "uTime": "1725054238441" + } + ] +} \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/mappings/api_v2_spot_public_symbols.json b/xchange-bitget/src/test/resources/mappings/api_v2_spot_public_symbols.json new file mode 100644 index 00000000000..428ffbcb55e --- /dev/null +++ b/xchange-bitget/src/test/resources/mappings/api_v2_spot_public_symbols.json @@ -0,0 +1,15 @@ +{ + "id" : "1a8a3149-0eb1-429f-a06a-4a2242e18d42", + "name" : "api_v2_spot_public_symbols", + "request" : { + "url" : "/api/v2/spot/public/symbols", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v2_spot_public_symbols.json" + }, + "uuid" : "1a8a3149-0eb1-429f-a06a-4a2242e18d42", + "persistent" : true, + "insertionIndex" : 2 +} \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json b/xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json new file mode 100644 index 00000000000..2baab142f3c --- /dev/null +++ b/xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json @@ -0,0 +1,15 @@ +{ + "id" : "aebaa168-bbc9-4819-98f5-c2336d01ca43", + "name" : "api_v2_spot_trade_orderinfo", + "request" : { + "url" : "/api/v2/spot/trade/orderInfo?orderId=1213530920130613257", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json" + }, + "uuid" : "aebaa168-bbc9-4819-98f5-c2336d01ca43", + "persistent" : true, + "insertionIndex" : 1 +} \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/rest/spot.http b/xchange-bitget/src/test/resources/rest/spot.http index c5c1e333b2d..3a658692faa 100644 --- a/xchange-bitget/src/test/resources/rest/spot.http +++ b/xchange-bitget/src/test/resources/rest/spot.http @@ -38,3 +38,57 @@ ACCESS-TIMESTAMP: {{timestamp}} ACCESS-PASSPHRASE: {{api_passphrase}} +### Get Order Info +< {% + import {gen_sign} from 'sign.js' + gen_sign("GET", request); +%} +GET {{api_host}}/api/v2/spot/trade/orderInfo?orderId=1213530920130613257 +ACCESS-KEY: {{api_key}} +ACCESS-SIGN: {{sign}} +ACCESS-TIMESTAMP: {{timestamp}} +ACCESS-PASSPHRASE: {{api_passphrase}} + + +### Place Market Buy Order +< {% + import {gen_sign} from 'sign.js' + gen_sign("POST", request); +%} +POST {{api_host}}/api/v2/spot/trade/place-order +ACCESS-KEY: {{api_key}} +ACCESS-SIGN: {{sign}} +ACCESS-TIMESTAMP: {{timestamp}} +ACCESS-PASSPHRASE: {{api_passphrase}} +Content-Type: application/json + +{ + "symbol": "GOMININGUSDT", + "side": "buy", + "orderType": "market", + "size": "2", + "clientOid": "{{$random.uuid}}" +} + + +### Place Market Sell Order +< {% + import {gen_sign} from 'sign.js' + gen_sign("POST", request); +%} +POST {{api_host}}/api/v2/spot/trade/place-order +ACCESS-KEY: {{api_key}} +ACCESS-SIGN: {{sign}} +ACCESS-TIMESTAMP: {{timestamp}} +ACCESS-PASSPHRASE: {{api_passphrase}} +Content-Type: application/json + +{ + "symbol": "GOMININGUSDT", + "side": "sell", + "orderType": "market", + "size": "3", + "clientOid": "{{$random.uuid}}" +} + + From 178e5f296f5e8a2fb64fbabc5c7ca493b5b6274d Mon Sep 17 00:00:00 2001 From: Dmitri Karpovich Date: Sun, 1 Sep 2024 20:29:36 +0200 Subject: [PATCH 8/8] [bitget] Add placing of market orders --- .../knowm/xchange/bitget/BitgetAdapters.java | 12 ++ .../xchange/bitget/BitgetAuthenticated.java | 15 +++ .../xchange/bitget/BitgetErrorAdapter.java | 9 +- .../converter/OrderTypeToStringConverter.java | 20 ++++ .../bitget/dto/trade/BitgetPlaceOrderDto.java | 111 ++++++++++++++++++ .../bitget/service/BitgetTradeService.java | 14 +++ .../bitget/service/BitgetTradeServiceRaw.java | 6 + .../service/BitgetTradeServiceTest.java | 63 ++++++++-- .../__files/api_v2_spot_public_symbols.json | 12 +- ...de_orderinfo-t-valid-market-buy-order.json | 30 +++++ ...e_orderinfo-t-valid-market-sell-order.json | 22 ++-- .../api_v2_spot_trade_place-buy-order.json | 9 ++ .../api_v2_spot_trade_place-sell-order.json | 9 ++ ...de_orderinfo-t-valid-market-buy-order.json | 15 +++ ...e_orderinfo-t-valid-market-sell-order.json | 2 +- .../api_v2_spot_trade_place-buy-order.json | 20 ++++ .../api_v2_spot_trade_place-sell-order.json | 20 ++++ .../src/test/resources/rest/spot.http | 4 +- 18 files changed, 363 insertions(+), 30 deletions(-) create mode 100644 xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/converter/OrderTypeToStringConverter.java create mode 100755 xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/trade/BitgetPlaceOrderDto.java create mode 100644 xchange-bitget/src/test/resources/__files/api_v2_spot_trade_orderinfo-t-valid-market-buy-order.json create mode 100644 xchange-bitget/src/test/resources/__files/api_v2_spot_trade_place-buy-order.json create mode 100644 xchange-bitget/src/test/resources/__files/api_v2_spot_trade_place-sell-order.json create mode 100644 xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_orderinfo-t-valid-market-buy-order.json create mode 100644 xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_place-buy-order.json create mode 100644 xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_place-sell-order.json diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java index 1a72a2bd870..61ab482b919 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAdapters.java @@ -17,6 +17,7 @@ import org.knowm.xchange.bitget.dto.marketdata.BitgetTickerDto; import org.knowm.xchange.bitget.dto.trade.BitgetOrderInfoDto; import org.knowm.xchange.bitget.dto.trade.BitgetOrderInfoDto.BitgetOrderStatus; +import org.knowm.xchange.bitget.dto.trade.BitgetPlaceOrderDto; import org.knowm.xchange.currency.Currency; import org.knowm.xchange.currency.CurrencyPair; import org.knowm.xchange.dto.Order; @@ -219,4 +220,15 @@ public OrderStatus toOrderStatus(BitgetOrderStatus bitgetOrderStatus) { } + public BitgetPlaceOrderDto toBitgetPlaceOrderDto(MarketOrder marketOrder) { + return BitgetPlaceOrderDto.builder() + .symbol(toString(marketOrder.getInstrument())) + .orderSide(marketOrder.getType()) + .orderType(BitgetOrderInfoDto.OrderType.MARKET) + .clientOid(marketOrder.getUserReference()) + .size(marketOrder.getOriginalAmount()) + .build(); + } + + } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAuthenticated.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAuthenticated.java index d9368782ecd..a22f8f75a10 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAuthenticated.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetAuthenticated.java @@ -1,7 +1,9 @@ package org.knowm.xchange.bitget; +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.Produces; import jakarta.ws.rs.QueryParam; @@ -12,6 +14,7 @@ import org.knowm.xchange.bitget.dto.BitgetResponse; import org.knowm.xchange.bitget.dto.account.BitgetBalanceDto; import org.knowm.xchange.bitget.dto.trade.BitgetOrderInfoDto; +import org.knowm.xchange.bitget.dto.trade.BitgetPlaceOrderDto; import si.mazi.rescu.ParamsDigest; import si.mazi.rescu.SynchronizedValueFactory; @@ -40,4 +43,16 @@ BitgetResponse> orderInfo( throws IOException, BitgetException; + @POST + @Path("api/v2/spot/trade/place-order") + @Consumes(MediaType.APPLICATION_JSON) + BitgetResponse createOrder( + @HeaderParam("ACCESS-KEY") String apiKey, + @HeaderParam("ACCESS-SIGN") ParamsDigest signer, + @HeaderParam("ACCESS-PASSPHRASE") String passphrase, + @HeaderParam("ACCESS-TIMESTAMP") SynchronizedValueFactory timestamp, + BitgetPlaceOrderDto bitgetPlaceOrderDto) + throws IOException, BitgetException; + + } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetErrorAdapter.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetErrorAdapter.java index 34755fab16c..3d50923b042 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetErrorAdapter.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/BitgetErrorAdapter.java @@ -3,21 +3,23 @@ import lombok.experimental.UtilityClass; import org.knowm.xchange.bitget.dto.BitgetException; import org.knowm.xchange.exceptions.ExchangeException; +import org.knowm.xchange.exceptions.FundsExceededException; import org.knowm.xchange.exceptions.InstrumentNotValidException; import org.knowm.xchange.exceptions.OrderAmountUnderMinimumException; @UtilityClass public class BitgetErrorAdapter { - public final int INVALID_PARAMETER_CODE = 40034; + public final int INVALID_PARAMETER = 40034; public final int MIN_ORDER_SIZE = 13008; public final int MIN_ORDER_AMOUNT = 45110; public final int MIN_ORDER_QTY = 45111; + public final int INSUFFICIENT_BALANCE = 43012; public ExchangeException adapt(BitgetException e) { switch (e.getCode()) { - case INVALID_PARAMETER_CODE: + case INVALID_PARAMETER: return new InstrumentNotValidException(e.getMessage(), e); case MIN_ORDER_SIZE: @@ -25,6 +27,9 @@ public ExchangeException adapt(BitgetException e) { case MIN_ORDER_QTY: return new OrderAmountUnderMinimumException(e.getMessage(), e); + case INSUFFICIENT_BALANCE: + return new FundsExceededException(e.getMessage(), e); + default: return new ExchangeException(e.getMessage(), e); } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/converter/OrderTypeToStringConverter.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/converter/OrderTypeToStringConverter.java new file mode 100644 index 00000000000..abe591c7f3b --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/config/converter/OrderTypeToStringConverter.java @@ -0,0 +1,20 @@ +package org.knowm.xchange.bitget.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-bitget/src/main/java/org/knowm/xchange/bitget/dto/trade/BitgetPlaceOrderDto.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/trade/BitgetPlaceOrderDto.java new file mode 100755 index 00000000000..b66048be66d --- /dev/null +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/dto/trade/BitgetPlaceOrderDto.java @@ -0,0 +1,111 @@ +package org.knowm.xchange.bitget.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.bitget.config.converter.OrderTypeToStringConverter; +import org.knowm.xchange.bitget.config.converter.StringToOrderTypeConverter; +import org.knowm.xchange.bitget.dto.trade.BitgetOrderInfoDto.OrderType; +import org.knowm.xchange.dto.Order; + +@Data +@Builder +@Jacksonized +public class BitgetPlaceOrderDto { + + @JsonProperty("symbol") + private String symbol; + + @JsonProperty("side") + @JsonDeserialize(converter = StringToOrderTypeConverter.class) + @JsonSerialize(converter = OrderTypeToStringConverter.class) + private Order.OrderType orderSide; + + @JsonProperty("orderType") + private OrderType orderType; + + @JsonProperty("force") + private TimeInForce timeInForce; + + @JsonProperty("price") + private BigDecimal price; + + @JsonProperty("size") + private BigDecimal size; + + @JsonProperty("clientOid") + private String clientOid; + + @JsonProperty("triggerPrice") + private BigDecimal triggerPrice; + + @JsonProperty("tpslType") + private TpSlType tpSlType; + + @JsonProperty("requestTime") + private Instant requestTime; + + @JsonProperty("receiveWindow") + private Instant receiveWindow; + + @JsonProperty("stpMode") + private StpMode stpMode; + + @JsonProperty("presetTakeProfitPrice") + private BigDecimal presetTakeProfitPrice; + + @JsonProperty("executeTakeProfitPrice") + private BigDecimal executeTakeProfitPrice; + + @JsonProperty("presetStopLossPrice") + private BigDecimal presetStopLossPrice; + + @JsonProperty("executeStopLossPrice") + private BigDecimal executeStopLossPrice; + + + public enum TimeInForce { + @JsonProperty("gtc") + GOOD_TIL_CANCELLED, + + @JsonProperty("post_only") + POST_ONLY, + + @JsonProperty("fok") + FILL_OR_KILL, + + @JsonProperty("ioc") + IMMEDIATE_OR_CANCEL + } + + + public enum TpSlType { + @JsonProperty("normal") + NORMAL, + + @JsonProperty("tpsl") + SPOT_TP_SL + } + + + public enum StpMode { + @JsonProperty("none") + NONE, + + @JsonProperty("cancel_taker") + CANCEL_TAKER, + + @JsonProperty("cancel_maker") + CANCEL_MAKER, + + @JsonProperty("cancel_both") + CANCEL_BOTH + } + + +} diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetTradeService.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetTradeService.java index 1b4f0933ce7..73a52bf5493 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetTradeService.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetTradeService.java @@ -10,6 +10,7 @@ import org.knowm.xchange.bitget.dto.BitgetException; import org.knowm.xchange.bitget.dto.trade.BitgetOrderInfoDto; import org.knowm.xchange.dto.Order; +import org.knowm.xchange.dto.trade.MarketOrder; import org.knowm.xchange.service.trade.TradeService; import org.knowm.xchange.service.trade.params.orders.DefaultQueryOrderParam; import org.knowm.xchange.service.trade.params.orders.OrderQueryParams; @@ -36,4 +37,17 @@ public Collection getOrder(OrderQueryParams... orderQueryParams) throws I } } + + + @Override + public String placeMarketOrder(MarketOrder marketOrder) throws IOException { + try { + return createOrder(BitgetAdapters.toBitgetPlaceOrderDto(marketOrder)).getOrderId(); + } + catch (BitgetException e) { + throw BitgetErrorAdapter.adapt(e); + } + } + + } diff --git a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetTradeServiceRaw.java b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetTradeServiceRaw.java index f859f2fb011..870d26c6494 100644 --- a/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetTradeServiceRaw.java +++ b/xchange-bitget/src/main/java/org/knowm/xchange/bitget/service/BitgetTradeServiceRaw.java @@ -4,6 +4,7 @@ import java.util.List; import org.knowm.xchange.bitget.BitgetExchange; import org.knowm.xchange.bitget.dto.trade.BitgetOrderInfoDto; +import org.knowm.xchange.bitget.dto.trade.BitgetPlaceOrderDto; public class BitgetTradeServiceRaw extends BitgetBaseService { @@ -21,4 +22,9 @@ public BitgetOrderInfoDto bitgetOrderInfoDto(String orderId) throws IOException return results.get(0); } + + public BitgetOrderInfoDto createOrder(BitgetPlaceOrderDto bitgetPlaceOrderDto) throws IOException { + return bitgetAuthenticated.createOrder(apiKey, bitgetDigest, passphrase, exchange.getNonceFactory(), bitgetPlaceOrderDto).getData(); + } + } diff --git a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetTradeServiceTest.java b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetTradeServiceTest.java index e6f70a4f078..50356073bd0 100644 --- a/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetTradeServiceTest.java +++ b/xchange-bitget/src/test/java/org/knowm/xchange/bitget/service/BitgetTradeServiceTest.java @@ -15,6 +15,7 @@ import org.knowm.xchange.dto.Order.OrderType; import org.knowm.xchange.dto.trade.MarketOrder; import org.knowm.xchange.service.trade.TradeService; +import org.knowm.xchange.service.trade.params.orders.DefaultQueryOrderParamInstrument; class BitgetTradeServiceTest extends BitgetExchangeWiremock { @@ -24,18 +25,18 @@ class BitgetTradeServiceTest extends BitgetExchangeWiremock { @Test void sell_order_details() throws IOException { MarketOrder expected = - new MarketOrder.Builder(OrderType.ASK, new CurrencyPair("GOMINING/USDT")) - .id("1213530920130613257") + new MarketOrder.Builder(OrderType.ASK, new CurrencyPair("BGB/USDT")) + .id("1214193970718347264") .userReference("t-valid-market-sell-order") - .timestamp(Date.from(Instant.parse("2024-08-30T21:43:58.350Z"))) - .originalAmount(new BigDecimal("3")) + .timestamp(Date.from(Instant.parse("2024-09-01T17:38:41.929Z"))) + .originalAmount(new BigDecimal("2")) .orderStatus(OrderStatus.FILLED) - .cumulativeAmount(new BigDecimal("3")) - .averagePrice(new BigDecimal("0.37846")) - .fee(new BigDecimal("0.00113538")) + .cumulativeAmount(new BigDecimal("2")) + .averagePrice(new BigDecimal("0.9649")) + .fee(new BigDecimal("0.0019298")) .build(); - Collection orders = tradeService.getOrder("1213530920130613257"); + Collection orders = tradeService.getOrder("1214193970718347264"); assertThat(orders).hasSize(1); assertThat(orders).first() .usingComparatorForType(BigDecimal::compareTo, BigDecimal.class) @@ -43,6 +44,52 @@ void sell_order_details() throws IOException { } + @Test + void buy_order_details() throws IOException { + MarketOrder expected = + new MarketOrder.Builder(OrderType.BID, new CurrencyPair("BGB/USDT")) + .id("1214189703404097539") + .userReference("t-valid-market-buy-order") + .timestamp(Date.from(Instant.parse("2024-09-01T17:21:44.522Z"))) + .originalAmount(new BigDecimal("2")) + .orderStatus(OrderStatus.FILLED) + .cumulativeAmount(new BigDecimal("1.9999925400000000")) + .averagePrice(new BigDecimal("0.9659000000000000")) + .fee(new BigDecimal("0.0020706")) + .build(); + + Collection orders = tradeService.getOrder(new DefaultQueryOrderParamInstrument(null, "1214189703404097539")); + assertThat(orders).hasSize(1); + assertThat(orders).first() + .usingComparatorForType(BigDecimal::compareTo, BigDecimal.class) + .usingRecursiveComparison().isEqualTo(expected); + } + + + @Test + void place_market_buy_order() throws IOException { + MarketOrder marketOrder = + new MarketOrder.Builder(OrderType.BID, new CurrencyPair("BGB/USDT")) + .userReference("t-valid-market-buy-order") + .originalAmount(BigDecimal.valueOf(2)) + .build(); + + String actualResponse = exchange.getTradeService().placeMarketOrder(marketOrder); + assertThat(actualResponse).isEqualTo("1214189703404097539"); + } + + + @Test + void place_market_sell_order() throws IOException { + MarketOrder marketOrder = + new MarketOrder.Builder(OrderType.ASK, new CurrencyPair("BGB/USDT")) + .userReference("t-valid-market-sell-order") + .originalAmount(BigDecimal.valueOf(2)) + .build(); + + String actualResponse = exchange.getTradeService().placeMarketOrder(marketOrder); + assertThat(actualResponse).isEqualTo("1214193970718347264"); + } } \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/__files/api_v2_spot_public_symbols.json b/xchange-bitget/src/test/resources/__files/api_v2_spot_public_symbols.json index 2ad8c766358..7c262633a47 100644 --- a/xchange-bitget/src/test/resources/__files/api_v2_spot_public_symbols.json +++ b/xchange-bitget/src/test/resources/__files/api_v2_spot_public_symbols.json @@ -38,20 +38,20 @@ "areaSymbol": "no" }, { - "symbol": "GOMININGUSDT", - "baseCoin": "GOMINING", + "symbol": "BGBUSDT", + "baseCoin": "BGB", "quoteCoin": "USDT", "minTradeAmount": "0", "maxTradeAmount": "10000000000", "takerFeeRate": "0.001", "makerFeeRate": "0.001", - "pricePrecision": "5", - "quantityPrecision": "2", + "pricePrecision": "4", + "quantityPrecision": "4", "quotePrecision": "6", "status": "online", "minTradeUSDT": "1", - "buyLimitPriceRatio": "10", - "sellLimitPriceRatio": "0.9", + "buyLimitPriceRatio": "0.05", + "sellLimitPriceRatio": "0.05", "areaSymbol": "no" } ] diff --git a/xchange-bitget/src/test/resources/__files/api_v2_spot_trade_orderinfo-t-valid-market-buy-order.json b/xchange-bitget/src/test/resources/__files/api_v2_spot_trade_orderinfo-t-valid-market-buy-order.json new file mode 100644 index 00000000000..6c03d91113d --- /dev/null +++ b/xchange-bitget/src/test/resources/__files/api_v2_spot_trade_orderinfo-t-valid-market-buy-order.json @@ -0,0 +1,30 @@ +{ + "code": "00000", + "msg": "success", + "requestTime": 1725211448919, + "data": [ + { + "userId": "1548914322", + "symbol": "BGBUSDT", + "orderId": "1214189703404097539", + "clientOid": "t-valid-market-buy-order", + "price": "0", + "size": "2.0000000000000000", + "orderType": "market", + "side": "buy", + "status": "filled", + "priceAvg": "0.9659000000000000", + "baseVolume": "2.0706000000000000", + "quoteVolume": "1.9999925400000000", + "enterPointSource": "API", + "feeDetail": "{\"BGB\":{\"deduction\":false,\"feeCoinCode\":\"BGB\",\"totalDeductionFee\":0,\"totalFee\":-0.0020706000000000},\"newFees\":{\"c\":0,\"d\":0,\"deduction\":false,\"r\":-0.0020706,\"t\":-0.0020706,\"totalDeductionFee\":0}}", + "orderSource": "market", + "tpslType": "normal", + "triggerPrice": null, + "quoteCoin": "USDT", + "baseCoin": "BGB", + "cTime": "1725211304522", + "uTime": "1725211304609" + } + ] +} \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/__files/api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json b/xchange-bitget/src/test/resources/__files/api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json index 733b02ca45a..256eae53494 100644 --- a/xchange-bitget/src/test/resources/__files/api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json +++ b/xchange-bitget/src/test/resources/__files/api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json @@ -1,30 +1,30 @@ { "code": "00000", "msg": "success", - "requestTime": 1725192128082, + "requestTime": 1725212468324, "data": [ { "userId": "1548914322", - "symbol": "GOMININGUSDT", - "orderId": "1213530920130613257", + "symbol": "BGBUSDT", + "orderId": "1214193970718347264", "clientOid": "t-valid-market-sell-order", "price": "0", - "size": "3.0000000000000000", + "size": "2.0000000000000000", "orderType": "market", "side": "sell", "status": "filled", - "priceAvg": "0.3784600000000000", - "baseVolume": "3.0000000000000000", - "quoteVolume": "1.1353800000000000", + "priceAvg": "0.9649000000000000", + "baseVolume": "2.0000000000000000", + "quoteVolume": "1.9298000000000000", "enterPointSource": "API", - "feeDetail": "{\"newFees\":{\"c\":0,\"d\":0,\"deduction\":false,\"r\":-0.00113538,\"t\":-0.00113538,\"totalDeductionFee\":0},\"USDT\":{\"deduction\":false,\"feeCoinCode\":\"USDT\",\"totalDeductionFee\":0,\"totalFee\":-0.0011353800000000}}", + "feeDetail": "{\"newFees\":{\"c\":0,\"d\":0,\"deduction\":false,\"r\":-0.0019298,\"t\":-0.0019298,\"totalDeductionFee\":0},\"USDT\":{\"deduction\":false,\"feeCoinCode\":\"USDT\",\"totalDeductionFee\":0,\"totalFee\":-0.0019298000000000}}", "orderSource": "market", "tpslType": "normal", "triggerPrice": null, "quoteCoin": "USDT", - "baseCoin": "GOMINING", - "cTime": "1725054238350", - "uTime": "1725054238441" + "baseCoin": "BGB", + "cTime": "1725212321929", + "uTime": "1725212322028" } ] } \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/__files/api_v2_spot_trade_place-buy-order.json b/xchange-bitget/src/test/resources/__files/api_v2_spot_trade_place-buy-order.json new file mode 100644 index 00000000000..7a3a3b197b1 --- /dev/null +++ b/xchange-bitget/src/test/resources/__files/api_v2_spot_trade_place-buy-order.json @@ -0,0 +1,9 @@ +{ + "code": "00000", + "msg": "success", + "requestTime": 1725211304517, + "data": { + "orderId": "1214189703404097539", + "clientOid": "t-valid-market-buy-order" + } +} \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/__files/api_v2_spot_trade_place-sell-order.json b/xchange-bitget/src/test/resources/__files/api_v2_spot_trade_place-sell-order.json new file mode 100644 index 00000000000..0a7cf32d17b --- /dev/null +++ b/xchange-bitget/src/test/resources/__files/api_v2_spot_trade_place-sell-order.json @@ -0,0 +1,9 @@ +{ + "code": "00000", + "msg": "success", + "requestTime": 1725212321924, + "data": { + "orderId": "1214193970718347264", + "clientOid": "t-valid-market-sell-order" + } +} \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_orderinfo-t-valid-market-buy-order.json b/xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_orderinfo-t-valid-market-buy-order.json new file mode 100644 index 00000000000..f7d02b466a6 --- /dev/null +++ b/xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_orderinfo-t-valid-market-buy-order.json @@ -0,0 +1,15 @@ +{ + "id" : "aebaa168-bbc9-4819-98f5-c2336d01ca43", + "name" : "api_v2_spot_trade_orderinfo", + "request" : { + "url" : "/api/v2/spot/trade/orderInfo?orderId=1214189703404097539", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v2_spot_trade_orderinfo-t-valid-market-buy-order.json" + }, + "uuid" : "aebaa168-bbc9-4819-98f5-c2336d01ca43", + "persistent" : true, + "insertionIndex" : 1 +} \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json b/xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json index 2baab142f3c..e9789b0feb9 100644 --- a/xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json +++ b/xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_orderinfo-t-valid-market-sell-order.json @@ -2,7 +2,7 @@ "id" : "aebaa168-bbc9-4819-98f5-c2336d01ca43", "name" : "api_v2_spot_trade_orderinfo", "request" : { - "url" : "/api/v2/spot/trade/orderInfo?orderId=1213530920130613257", + "url" : "/api/v2/spot/trade/orderInfo?orderId=1214193970718347264", "method" : "GET" }, "response" : { diff --git a/xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_place-buy-order.json b/xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_place-buy-order.json new file mode 100644 index 00000000000..a6b55d3d18d --- /dev/null +++ b/xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_place-buy-order.json @@ -0,0 +1,20 @@ +{ + "id" : "bf637b08-636e-4c43-af21-90ccb86586f4", + "name" : "api_v2_spot_trade_place-order", + "request" : { + "url" : "/api/v2/spot/trade/place-order", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"symbol\":\"BGBUSDT\",\"side\":\"buy\",\"orderType\":\"market\",\"size\":2,\"clientOid\":\"t-valid-market-buy-order\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v2_spot_trade_place-buy-order.json" + }, + "uuid" : "bf637b08-636e-4c43-af21-90ccb86586f4", + "persistent" : true, + "insertionIndex" : 3 +} \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_place-sell-order.json b/xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_place-sell-order.json new file mode 100644 index 00000000000..a951b592db8 --- /dev/null +++ b/xchange-bitget/src/test/resources/mappings/api_v2_spot_trade_place-sell-order.json @@ -0,0 +1,20 @@ +{ + "id" : "c04a9a18-7a57-4f15-86e4-b32cf57cea4e", + "name" : "api_v2_spot_trade_place-order", + "request" : { + "url" : "/api/v2/spot/trade/place-order", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"symbol\":\"BGBUSDT\",\"side\":\"sell\",\"orderType\":\"market\",\"size\":2,\"clientOid\":\"t-valid-market-sell-order\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v2_spot_trade_place-sell-order.json" + }, + "uuid" : "c04a9a18-7a57-4f15-86e4-b32cf57cea4e", + "persistent" : true, + "insertionIndex" : 5 +} \ No newline at end of file diff --git a/xchange-bitget/src/test/resources/rest/spot.http b/xchange-bitget/src/test/resources/rest/spot.http index 3a658692faa..1fb8850c39f 100644 --- a/xchange-bitget/src/test/resources/rest/spot.http +++ b/xchange-bitget/src/test/resources/rest/spot.http @@ -63,7 +63,7 @@ ACCESS-PASSPHRASE: {{api_passphrase}} Content-Type: application/json { - "symbol": "GOMININGUSDT", + "symbol": "BGBUSDT", "side": "buy", "orderType": "market", "size": "2", @@ -84,7 +84,7 @@ ACCESS-PASSPHRASE: {{api_passphrase}} Content-Type: application/json { - "symbol": "GOMININGUSDT", + "symbol": "BGBUSDT", "side": "sell", "orderType": "market", "size": "3",