records = new ArrayList<>();
+
+ for (CoinbaseBuySell record : trades) {
+ records.add(adaptFunding(record));
+ }
+
+ return records;
+ }
+
+ private static FundingRecord adaptFunding(CoinbaseBuySell transaction) {
+
+ FundingRecord.Type type = null;
+ FundingRecord.Status status;
+ String recordType = transaction.getResource().toUpperCase();
+
+ switch (recordType) {
+ case "WITHDRAWAL":
+ case "CREATE_VOUCHER":
+ type = FundingRecord.Type.WITHDRAWAL;
+ break;
+ case "DEPOSIT":
+ case "USED_VOUCHER":
+ case "NEW_USER_REWARD":
+ case "REFERRAL":
+ type = FundingRecord.Type.DEPOSIT;
+ break;
+ default:
+ // here we ignore the other types which are trading
+ }
+
+ switch (transaction.getStatus().toUpperCase()) {
+ case "OK":
+ case "COMPLETED":
+ status = FundingRecord.Status.COMPLETE;
+ break;
+ case "NEW":
+ case "SENT":
+ case "CREATED":
+ case "WAITING":
+ case "PENDING":
+ status = FundingRecord.Status.PROCESSING;
+ break;
+ default:
+ status = FundingRecord.Status.FAILED;
+ }
+
+ FundingRecord funding =
+ new FundingRecord(
+ null,
+ Date.from(transaction.getCreatedAt().toInstant()),
+ Currency.getInstance(transaction.getAmount().getCurrency()),
+ transaction.getAmount().getAmount(),
+ transaction.getId(),
+ null,
+ type,
+ status,
+ null,
+ transaction.getFee().getAmount(),
+ null);
+ return funding;
+ }
+
private static UserTrade adaptTrade(CoinbaseBuySell transaction, OrderType orderType) {
- return UserTrade.builder()
+ // Bug fix - Null point exception in case of cancelled transactions
+
+ String transactionId = transaction.getTransaction() == null ?
+ null : (transaction.getTransaction().getId() == null ?
+ null : transaction.getTransaction().getId());
+ return new UserTrade.Builder()
.type(orderType)
.originalAmount(transaction.getAmount().getAmount())
.currencyPair(
@@ -70,7 +139,7 @@ private static UserTrade adaptTrade(CoinbaseBuySell transaction, OrderType order
.divide(transaction.getAmount().getAmount(), PRICE_SCALE, RoundingMode.HALF_UP))
.timestamp(Date.from(transaction.getCreatedAt().toInstant()))
.id(transaction.getId())
- .orderId(transaction.getTransaction().getId())
+ .orderId(transactionId)
.feeAmount(transaction.getFee().getAmount())
.feeCurrency(Currency.getInstance(transaction.getFee().getCurrency()))
.build();
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/CoinbaseAuthenticatedCDP.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/CoinbaseAuthenticatedCDP.java
new file mode 100644
index 00000000000..229810d292e
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/CoinbaseAuthenticatedCDP.java
@@ -0,0 +1,189 @@
+package org.knowm.xchange.coinbase.cdp;
+
+import org.knowm.xchange.coinbase.v2.Coinbase;
+import org.knowm.xchange.coinbase.v2.dto.CoinbaseException;
+import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseAccountData;
+import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseAccountsData;
+import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseBuyData;
+import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseExpandTransactionsResponse;
+import org.knowm.xchange.coinbase.v2.dto.account.CoinbasePaymentMethodsData;
+import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseSellData;
+import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseTransactionsResponse;
+import org.knowm.xchange.coinbase.v2.dto.account.transactions.CoinbaseBuySellResponse;
+import si.mazi.rescu.ParamsDigest;
+
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.Map;
+
+@Path("/v2")
+@Produces(MediaType.APPLICATION_JSON)
+public interface CoinbaseAuthenticatedCDP extends Coinbase {
+
+ /**
+ * All API key requests must be signed and contain the following headers.
+ *
+ * All request bodies should have content type application/json and be valid JSON.
+ *
+ *
The CB-ACCESS-SIGN header is generated by creating a sha256 HMAC using the secret key on the
+ * prehash string timestamp + method + requestPath + body (where + represents string
+ * concatenation). The timestamp value is the same as the CB-ACCESS-TIMESTAMP header.
+ *
+ *
The body is the request body string or omitted if there is no request body (typically for
+ * GET requests).
+ *
+ *
The method should be UPPER CASE.
+ *
+ *
developers.coinbase.com/api/v2#api-key
+ */
+ String CB_ACCESS_KEY = "CB-ACCESS-KEY";
+
+
+ String CB_ACCESS_SIGN = "CB-ACCESS-SIGN";
+ String CB_ACCESS_TIMESTAMP = "CB-ACCESS-TIMESTAMP";
+
+ String CONTENT_TYPE = "Content-Type";
+
+ @GET
+ @Path("accounts/{accountId}/transactions")
+ CoinbaseTransactionsResponse getTransactions(
+ @HeaderParam("Authorization") ParamsDigest signature,
+ @PathParam("accountId") String accountId)
+ throws IOException, CoinbaseException;
+
+ @GET
+ @Path("accounts/{accountId}/transactions")
+ CoinbaseExpandTransactionsResponse getExpandedTransactions(
+ @HeaderParam("Authorization") ParamsDigest signature,
+ @PathParam("accountId") String accountId,
+ @QueryParam("limit") int limit,
+ @QueryParam("order") String orderType,
+ @QueryParam("starting_after") String startingFrom)
+ throws IOException, CoinbaseException;
+
+ @GET
+ @Path("accounts/{accountId}/buys")
+ CoinbaseBuySellResponse getBuys(
+ @HeaderParam("Authorization") ParamsDigest signature,
+ @PathParam("accountId") String accountId,
+ @QueryParam("limit") Integer limit,
+ @QueryParam("starting_after") String startingAfter)
+ throws IOException, CoinbaseException;
+
+ @GET
+ @Path("accounts/{accountId}/sells")
+ CoinbaseBuySellResponse getSells(
+ @HeaderParam("Authorization") ParamsDigest signature,
+ @PathParam("accountId") String accountId,
+ @QueryParam("limit") Integer limit,
+ @QueryParam("starting_after") String startingAfter)
+ throws IOException, CoinbaseException;
+
+ @GET
+ @Path("accounts/{accountId}/deposits")
+ CoinbaseBuySellResponse getAllDeposits(
+ @HeaderParam("Authorization") ParamsDigest signature,
+ @PathParam("accountId") String accountId,
+ @QueryParam("limit") Integer limit,
+ @QueryParam("starting_after") String startingAfter)
+ throws IOException, CoinbaseException;
+
+ @GET
+ @Path("accounts/{accountId}/withdrawals")
+ CoinbaseBuySellResponse getAllWithdrawals(
+ @HeaderParam("Authorization") ParamsDigest signature,
+ @PathParam("accountId") String accountId,
+ @QueryParam("limit") Integer limit,
+ @QueryParam("starting_after") String startingAfter)
+ throws IOException, CoinbaseException;
+
+ @GET
+ @Path("accounts/{accountId}/deposits")
+ Map getDeposits(
+ @HeaderParam(CB_VERSION) String apiVersion,
+ @HeaderParam(CB_ACCESS_KEY) String apiKey,
+ @HeaderParam(CB_ACCESS_SIGN) ParamsDigest signature,
+ @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp,
+ @PathParam("accountId") String accountId)
+ throws IOException, CoinbaseException;
+
+ @GET
+ @Path("accounts/{accountId}/withdrawals")
+ Map getWithdrawals(
+ @HeaderParam(CB_VERSION) String apiVersion,
+ @HeaderParam(CB_ACCESS_KEY) String apiKey,
+ @HeaderParam(CB_ACCESS_SIGN) ParamsDigest signature,
+ @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp,
+ @PathParam("accountId") String accountId)
+ throws IOException, CoinbaseException;
+
+ @GET
+ @Path("accounts")
+ CoinbaseAccountsData getAccounts(
+ @HeaderParam("Authorization") ParamsDigest signature,
+ @QueryParam("limit") Integer limit,
+ @QueryParam("starting_after") String starting_after)
+ throws IOException, CoinbaseException;
+
+ @GET
+ @Path("accounts/{currency}")
+ CoinbaseAccountData getAccount(
+ @HeaderParam("Authorization") ParamsDigest signature,
+ @PathParam("currency") String currency)
+ throws IOException, CoinbaseException;
+
+ @POST
+ @Path("accounts")
+ @Consumes(MediaType.APPLICATION_JSON)
+ CoinbaseAccountData createAccount(
+ @HeaderParam(CONTENT_TYPE) String contentType,
+ @HeaderParam(CB_VERSION) String apiVersion,
+ @HeaderParam(CB_ACCESS_KEY) String apiKey,
+ @HeaderParam(CB_ACCESS_SIGN) String signature,
+ @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp,
+ Object payload)
+ throws IOException, CoinbaseException;
+
+ @GET
+ @Path("payment-methods")
+ CoinbasePaymentMethodsData getPaymentMethods(
+ @HeaderParam("Authorization") ParamsDigest signature)
+ throws IOException, CoinbaseException;
+
+ @POST
+ @Path("accounts/{account}/buys")
+ @Consumes(MediaType.APPLICATION_JSON)
+ CoinbaseBuyData buy(
+ @HeaderParam(CONTENT_TYPE) String contentType,
+ @HeaderParam(CB_VERSION) String apiVersion,
+ @HeaderParam(CB_ACCESS_KEY) String apiKey,
+ @HeaderParam(CB_ACCESS_SIGN) String signature,
+ @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp,
+ @PathParam("account") String accountId,
+ Object payload)
+ throws IOException, CoinbaseException;
+
+ @POST
+ @Path("accounts/{account}/sells")
+ @Consumes(MediaType.APPLICATION_JSON)
+ CoinbaseSellData sell(
+ @HeaderParam(CONTENT_TYPE) String contentType,
+ @HeaderParam(CB_VERSION) String apiVersion,
+ @HeaderParam(CB_ACCESS_KEY) String apiKey,
+ @HeaderParam(CB_ACCESS_SIGN) String signature,
+ @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp,
+ @PathParam("account") String accountId,
+ Object payload)
+ throws IOException, CoinbaseException;
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/CoinbaseExchangeCDP.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/CoinbaseExchangeCDP.java
new file mode 100644
index 00000000000..cedb4c2ce9e
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/CoinbaseExchangeCDP.java
@@ -0,0 +1,30 @@
+package org.knowm.xchange.coinbase.cdp;
+
+import org.knowm.xchange.BaseExchange;
+import org.knowm.xchange.Exchange;
+import org.knowm.xchange.ExchangeSpecification;
+import org.knowm.xchange.coinbase.v2.service.CoinbaseMarketDataService;
+import org.knowm.xchange.coinbase.cdp.service.CoinbaseAccountServiceCDP;
+import org.knowm.xchange.coinbase.cdp.service.CoinbaseTradeServiceCDP;
+
+public class CoinbaseExchangeCDP extends BaseExchange implements Exchange {
+
+ @Override
+ protected void initServices() {
+ this.marketDataService = new CoinbaseMarketDataService(this);
+ this.accountService = new CoinbaseAccountServiceCDP(this);
+ this.tradeService = new CoinbaseTradeServiceCDP(this);
+ }
+
+ @Override
+ public ExchangeSpecification getDefaultExchangeSpecification() {
+
+ final ExchangeSpecification exchangeSpecification = new ExchangeSpecification(this.getClass());
+ exchangeSpecification.setSslUri("https://api.coinbase.com");
+ exchangeSpecification.setHost("api.coinbase.com");
+ exchangeSpecification.setExchangeName("Coinbase");
+ exchangeSpecification.setExchangeDescription(
+ "Founded in June of 2012, Coinbase is a bitcoin wallet and platform where merchants and consumers can transact with the new digital currency bitcoin.");
+ return exchangeSpecification;
+ }
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/CoinbaseV2DigestCDP.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/CoinbaseV2DigestCDP.java
new file mode 100644
index 00000000000..674ddac0e36
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/CoinbaseV2DigestCDP.java
@@ -0,0 +1,115 @@
+package org.knowm.xchange.coinbase.cdp;
+
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.JWSSigner;
+import com.nimbusds.jose.crypto.ECDSASigner;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.knowm.xchange.service.BaseParamsDigest;
+import si.mazi.rescu.RestInvocation;
+
+import java.io.StringReader;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.interfaces.ECPrivateKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Map;
+
+public class CoinbaseV2DigestCDP extends BaseParamsDigest {
+
+ private final String cdpPrivateKey;
+ private final String cdpName;
+
+ private CoinbaseV2DigestCDP(String cdpPrivateKey, String cdpName) {
+ super("nothing","HmacSHA256");
+ this.cdpPrivateKey = cdpPrivateKey;
+ this.cdpName = cdpName;
+ }
+
+ public static CoinbaseV2DigestCDP createInstance(String cdpPrivateKey, String cdpName) {
+ return (cdpPrivateKey == null || cdpName == null) ? null : new CoinbaseV2DigestCDP(cdpPrivateKey, cdpName);
+ }
+
+ @Override
+ public String digestParams(RestInvocation restInvocation) {
+ String path = restInvocation.getInvocationUrl().replaceFirst("https://", "").replaceAll("\\?" + restInvocation.getQueryString(), "");
+ String requestMethod = restInvocation.getHttpMethod();
+ try {
+ return generateJwt(path, cdpPrivateKey, cdpName, requestMethod);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to generate JWT", e);
+ }
+ }
+
+ public String generateJwt(String urlString, String cdpPrivateKey, String cdpName, String httpMethod) throws Exception {
+ // Register BouncyCastle as a security provider
+ Security.addProvider(new BouncyCastleProvider());
+
+ // Load environment variables
+ String privateKeyPEM = cdpPrivateKey.replace("\\n", "\n");
+ String name = cdpName;
+
+ // create header object
+ Map header = new HashMap<>();
+ header.put("alg", "ES256");
+ header.put("typ", "JWT");
+ header.put("kid", name);
+ header.put("nonce", String.valueOf(Instant.now().getEpochSecond()));
+
+ // create uri string for current request
+ String requestMethod = httpMethod;
+ String url = urlString;
+ String uri = requestMethod + " " + url;
+
+ // create data object
+ Map data = new HashMap<>();
+ data.put("iss", "cdp");
+ data.put("nbf", Instant.now().getEpochSecond());
+ data.put("exp", Instant.now().getEpochSecond() + 120);
+ data.put("sub", name);
+ data.put("uri", uri);
+
+ // Load private key
+ PEMParser pemParser = new PEMParser(new StringReader(privateKeyPEM));
+ JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
+ Object object = pemParser.readObject();
+ PrivateKey privateKey;
+
+ if (object instanceof PrivateKey) {
+ privateKey = (PrivateKey) object;
+ } else if (object instanceof org.bouncycastle.openssl.PEMKeyPair) {
+ privateKey = converter.getPrivateKey(((org.bouncycastle.openssl.PEMKeyPair) object).getPrivateKeyInfo());
+ } else {
+ throw new Exception("Unexpected private key format");
+ }
+ pemParser.close();
+
+ // Convert to ECPrivateKey
+ KeyFactory keyFactory = KeyFactory.getInstance("EC");
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
+ ECPrivateKey ecPrivateKey = (ECPrivateKey) keyFactory.generatePrivate(keySpec);
+
+ // create JWT
+ JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder();
+ for (Map.Entry entry : data.entrySet()) {
+ claimsSetBuilder.claim(entry.getKey(), entry.getValue());
+ }
+ JWTClaimsSet claimsSet = claimsSetBuilder.build();
+
+ JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.ES256).customParams(header).build();
+ SignedJWT signedJWT = new SignedJWT(jwsHeader, claimsSet);
+
+ JWSSigner signer = new ECDSASigner(ecPrivateKey);
+ signedJWT.sign(signer);
+
+ String sJWT = signedJWT.serialize();
+ return "Bearer " + sJWT;
+ }
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/service/CoinbaseAccountServiceCDP.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/service/CoinbaseAccountServiceCDP.java
new file mode 100644
index 00000000000..c2d8eb8fdd5
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/service/CoinbaseAccountServiceCDP.java
@@ -0,0 +1,109 @@
+package org.knowm.xchange.coinbase.cdp.service;
+
+import org.knowm.xchange.Exchange;
+import org.knowm.xchange.coinbase.CoinbaseAdapters;
+import org.knowm.xchange.coinbase.v2.service.CoinbaseTradeHistoryParams;
+import org.knowm.xchange.coinbase.v2.Coinbase;
+import org.knowm.xchange.coinbase.v2.dto.CoinbaseAmount;
+import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseAccountData;
+import org.knowm.xchange.coinbase.v2.dto.account.transactions.CoinbaseBuySellResponse;
+import org.knowm.xchange.currency.Currency;
+import org.knowm.xchange.dto.account.AccountInfo;
+import org.knowm.xchange.dto.account.Balance;
+import org.knowm.xchange.dto.account.FundingRecord;
+import org.knowm.xchange.dto.account.Wallet;
+import org.knowm.xchange.exceptions.ExchangeException;
+import org.knowm.xchange.exceptions.NotAvailableFromExchangeException;
+import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException;
+import org.knowm.xchange.service.account.AccountService;
+import org.knowm.xchange.service.trade.params.DefaultWithdrawFundsParams;
+import org.knowm.xchange.service.trade.params.TradeHistoryParams;
+import org.knowm.xchange.service.trade.params.WithdrawFundsParams;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public final class CoinbaseAccountServiceCDP extends CoinbaseAccountServiceRawCDP
+ implements AccountService {
+
+ public CoinbaseAccountServiceCDP(Exchange exchange) {
+
+ super(exchange);
+ }
+
+ @Override
+ public AccountInfo getAccountInfo() throws IOException {
+ List wallets = new ArrayList<>();
+
+ List coinbaseAccounts = getCoinbaseAccounts();
+ for (CoinbaseAccountData.CoinbaseAccount coinbaseAccount : coinbaseAccounts) {
+ CoinbaseAmount balance = coinbaseAccount.getBalance();
+ Wallet wallet =
+ Wallet.Builder.from(
+ Arrays.asList(
+ new Balance(
+ Currency.getInstance(balance.getCurrency()), balance.getAmount())))
+ .id(coinbaseAccount.getId())
+ .build();
+ wallets.add(wallet);
+ }
+
+ return new AccountInfo(wallets);
+ }
+
+ @Override
+ public String withdrawFunds(WithdrawFundsParams params)
+ throws ExchangeException, NotAvailableFromExchangeException,
+ NotYetImplementedForExchangeException, IOException {
+ if (params instanceof DefaultWithdrawFundsParams) {
+ DefaultWithdrawFundsParams defaultParams = (DefaultWithdrawFundsParams) params;
+ return withdrawFunds(
+ defaultParams.getCurrency(), defaultParams.getAmount(), defaultParams.getAddress());
+ }
+ throw new IllegalStateException("Don't know how to withdraw: " + params);
+ }
+
+ @Override
+ public TradeHistoryParams createFundingHistoryParams() {
+ return new CoinbaseTradeHistoryParams();
+ }
+
+ /**
+ * The Coinbase is not typical exchange. It has splitted buys and sells into wallets (accounts).
+ * To get it is necessary to know the accountId (wallet ID) see {@link AccountInfo#getWallets()}
+ */
+ public List getWithdrawalHistory(CoinbaseTradeHistoryParams params, String accountId)
+ throws IOException {
+ final String apiKey = exchange.getExchangeSpecification().getApiKey();
+ final BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch();
+ final CoinbaseBuySellResponse withdrawals =
+ coinbase.getAllWithdrawals(
+ signatureCreator2,
+ accountId,
+ params.getLimit(),
+ params.getStartId()
+ );
+ return CoinbaseAdapters.adaptFundings(withdrawals.getData());
+ }
+
+ /**
+ * The Coinbase is not typical exchange. It has splitted buys and sells into wallets (accounts).
+ * To get it is necessary to know the accountId (wallet ID) from {@link AccountInfo#getWallets()}
+ */
+ public List getDepositHistory(CoinbaseTradeHistoryParams params, String accountId)
+ throws IOException {
+ final String apiKey = exchange.getExchangeSpecification().getApiKey();
+ final BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch();
+ final CoinbaseBuySellResponse deposits =
+ coinbase.getAllDeposits(
+ signatureCreator2,
+ accountId,
+ params.getLimit(),
+ params.getStartId()
+ );
+ return CoinbaseAdapters.adaptFundings(deposits.getData());
+ }
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/service/CoinbaseAccountServiceRawCDP.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/service/CoinbaseAccountServiceRawCDP.java
new file mode 100644
index 00000000000..1efb2d366c9
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/service/CoinbaseAccountServiceRawCDP.java
@@ -0,0 +1,167 @@
+package org.knowm.xchange.coinbase.cdp.service;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.knowm.xchange.Exchange;
+import org.knowm.xchange.coinbase.v2.service.CoinbaseTradeHistoryParams;
+import org.knowm.xchange.coinbase.v2.Coinbase;
+import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseAccountData.CoinbaseAccount;
+import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseExpandTransactionsResponse;
+import org.knowm.xchange.coinbase.v2.dto.account.CoinbasePaymentMethodsData.CoinbasePaymentMethod;
+import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseTransactionsResponse;
+import org.knowm.xchange.currency.Currency;
+
+import javax.ws.rs.core.MediaType;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+public class CoinbaseAccountServiceRawCDP extends CoinbaseBaseServiceCDP {
+
+ public CoinbaseAccountServiceRawCDP(Exchange exchange) {
+ super(exchange);
+ }
+
+ public CoinbaseTransactionsResponse getTransactions(String accountId) throws IOException {
+
+ return coinbase.getTransactions(
+ signatureCreator2, accountId);
+ }
+
+ public CoinbaseExpandTransactionsResponse getExpandTransactions(String accountId, CoinbaseTradeHistoryParams params, String orderType)
+ throws IOException {
+
+ return coinbase.getExpandedTransactions(
+ signatureCreator2,
+ accountId,
+ params.getLimit(),
+ orderType,
+ params.getStartId());
+ }
+
+ public Map getDeposits(String accountId) throws IOException {
+ String apiKey = exchange.getExchangeSpecification().getApiKey();
+ BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch();
+
+ return coinbase.getDeposits(
+ Coinbase.CB_VERSION_VALUE, apiKey, signatureCreator2, timestamp, accountId);
+ }
+
+ public Map getWithdrawals(String accountId) throws IOException {
+ String apiKey = exchange.getExchangeSpecification().getApiKey();
+ BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch();
+
+ return coinbase.getWithdrawals(
+ Coinbase.CB_VERSION_VALUE, apiKey, signatureCreator2, timestamp, accountId);
+ }
+
+ /**
+ * Authenticated resource that shows the current user accounts.
+ *
+ * @see developers.coinbase.com/api/v2#list-accounts
+ */
+ public List getCoinbaseAccounts() throws IOException {
+
+ List returnList = new ArrayList<>();
+ List tmpList = null;
+
+ String lastAccount = null;
+ do {
+ try {
+ tmpList =
+ coinbase
+ .getAccounts(
+ signatureCreator2, 100, lastAccount)
+ .getData();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ lastAccount = null;
+ if (tmpList != null && tmpList.size() > 0) {
+ returnList.addAll(tmpList);
+ lastAccount = tmpList.get(tmpList.size() - 1).getId();
+ }
+
+ } while (lastAccount != null && isValidUUID(lastAccount));
+
+ return returnList;
+ }
+
+ private boolean isValidUUID(String uuid) {
+ try {
+ UUID.fromString(uuid);
+ return true;
+ } catch (IllegalArgumentException exception) {
+ return false;
+ }
+ }
+
+ /**
+ * Authenticated resource that shows the current user account for the give currency.
+ *
+ * @see developers.coinbase.com/api/v2#show-an-account
+ */
+ public CoinbaseAccount getCoinbaseAccount(Currency currency) throws IOException {
+
+ return coinbase
+ .getAccount(
+ signatureCreator2,
+ currency.getCurrencyCode())
+ .getData();
+ }
+
+ /**
+ * Authenticated resource that creates a new BTC account for the current user.
+ *
+ * @see developers.coinbase.com/api/v2#create-account
+ */
+ public CoinbaseAccount createCoinbaseAccount(String name) throws IOException {
+
+ CreateCoinbaseAccountPayload payload = new CreateCoinbaseAccountPayload(name);
+
+ String path = "/v2/accounts";
+ String apiKey = exchange.getExchangeSpecification().getApiKey();
+ BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch();
+ String body = new ObjectMapper().writeValueAsString(payload);
+ String signature = getSignature(timestamp, HttpMethod.POST, path, body);
+ showCurl(HttpMethod.POST, apiKey, timestamp, signature, path, body);
+
+ return coinbase
+ .createAccount(
+ MediaType.APPLICATION_JSON,
+ Coinbase.CB_VERSION_VALUE,
+ apiKey,
+ signature,
+ timestamp,
+ payload)
+ .getData();
+ }
+
+ /**
+ * Authenticated resource that shows the current user payment methods.
+ *
+ * @see developers.coinbase.com/api/v2?shell#list-payment-methods
+ */
+ public List getCoinbasePaymentMethods() throws IOException {
+
+ return coinbase
+ .getPaymentMethods(signatureCreator2)
+ .getData();
+ }
+
+ public static class CreateCoinbaseAccountPayload {
+ @JsonProperty String name;
+
+ CreateCoinbaseAccountPayload(String name) {
+ this.name = name;
+ }
+ }
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/service/CoinbaseBaseServiceCDP.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/service/CoinbaseBaseServiceCDP.java
new file mode 100644
index 00000000000..5b459d5e937
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/service/CoinbaseBaseServiceCDP.java
@@ -0,0 +1,104 @@
+package org.knowm.xchange.coinbase.cdp.service;
+
+import org.knowm.xchange.Exchange;
+import org.knowm.xchange.client.ExchangeRestProxyBuilder;
+import org.knowm.xchange.coinbase.service.CoinbaseDigest;
+import org.knowm.xchange.coinbase.v3.CoinbaseAuthenticatedV3CDP;
+import org.knowm.xchange.coinbase.v2.Coinbase;
+import org.knowm.xchange.coinbase.cdp.CoinbaseAuthenticatedCDP;
+import org.knowm.xchange.coinbase.cdp.CoinbaseV2DigestCDP;
+import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseCurrencyData.CoinbaseCurrency;
+import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseTimeData.CoinbaseTime;
+import org.knowm.xchange.service.BaseExchangeService;
+import org.knowm.xchange.service.BaseService;
+import org.knowm.xchange.utils.DigestUtils;
+
+import javax.crypto.Mac;
+import javax.ws.rs.core.MediaType;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+public class CoinbaseBaseServiceCDP extends BaseExchangeService implements BaseService {
+
+ protected final CoinbaseAuthenticatedCDP coinbase;
+ protected final CoinbaseAuthenticatedV3CDP coinbaseV3;
+ protected final CoinbaseV2DigestCDP signatureCreator2;
+
+ protected CoinbaseBaseServiceCDP(Exchange exchange) {
+
+ super(exchange);
+ coinbase =
+ ExchangeRestProxyBuilder.forInterface(
+ CoinbaseAuthenticatedCDP.class, exchange.getExchangeSpecification())
+ .build();
+
+ coinbaseV3 =
+ ExchangeRestProxyBuilder.forInterface(
+ CoinbaseAuthenticatedV3CDP.class, exchange.getExchangeSpecification())
+ .build();
+
+ signatureCreator2 =
+ CoinbaseV2DigestCDP.createInstance(exchange.getExchangeSpecification().getSecretKey(), exchange.getExchangeSpecification().getApiKey());
+ }
+
+ /**
+ * Unauthenticated resource that returns currencies supported on Coinbase.
+ *
+ * @return A list of currency names and their corresponding ISO code.
+ * @see developers.coinbase.com/api/v2#get-currencies
+ */
+ public List getCoinbaseCurrencies() throws IOException {
+
+ return coinbase.getCurrencies(Coinbase.CB_VERSION_VALUE).getData();
+ }
+
+ /**
+ * Unauthenticated resource that tells you the server time.
+ *
+ * @return The current server time.
+ * @see developers.coinbase.com/api/v2#get-current-time
+ */
+ public CoinbaseTime getCoinbaseTime() throws IOException {
+
+ return coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData();
+ }
+
+ protected String getSignature(BigDecimal timestamp, HttpMethod method, String path, String body) {
+ String secretKey = exchange.getExchangeSpecification().getSecretKey();
+ String message = timestamp + method.toString() + path + (body != null ? body : "");
+ final Mac mac = CoinbaseDigest.createInstance(secretKey).getMac();
+ byte[] bytes = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
+ return DigestUtils.bytesToHex(bytes);
+ }
+
+ protected void showCurl(
+ HttpMethod method,
+ String apiKey,
+ BigDecimal timestamp,
+ String signature,
+ String path,
+ String json) {
+ String headers =
+ String.format(
+ "-H 'CB-VERSION: 2017-11-26' -H 'CB-ACCESS-KEY: %s' -H 'CB-ACCESS-SIGN: %s' -H 'CB-ACCESS-TIMESTAMP: %s'",
+ apiKey, signature, timestamp);
+ if (method == HttpMethod.GET) {
+ Coinbase.LOG.debug(String.format("curl %s https://api.coinbase.com%s", headers, path));
+ } else if (method == HttpMethod.POST) {
+ String payload = "-d '" + json + "'";
+ Coinbase.LOG.debug(
+ String.format(
+ "curl -X %s -H 'Content-Type: %s' %s %s https://api.coinbase.com%s",
+ method, MediaType.APPLICATION_JSON, headers, payload, path));
+ }
+ }
+
+ public enum HttpMethod {
+ GET,
+ POST
+ }
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/service/CoinbaseTradeServiceCDP.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/service/CoinbaseTradeServiceCDP.java
new file mode 100644
index 00000000000..fbee5f6b508
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/service/CoinbaseTradeServiceCDP.java
@@ -0,0 +1,107 @@
+package org.knowm.xchange.coinbase.cdp.service;
+
+import org.knowm.xchange.Exchange;
+import org.knowm.xchange.coinbase.CoinbaseAdapters;
+import org.knowm.xchange.coinbase.v2.dto.account.transactions.CoinbaseBuySellResponse;
+import org.knowm.xchange.coinbase.v2.service.CoinbaseTradeHistoryParams;
+import org.knowm.xchange.dto.Order;
+import org.knowm.xchange.dto.account.AccountInfo;
+import org.knowm.xchange.dto.trade.LimitOrder;
+import org.knowm.xchange.dto.trade.OpenOrders;
+import org.knowm.xchange.dto.trade.StopOrder;
+import org.knowm.xchange.dto.trade.UserTrades;
+import org.knowm.xchange.exceptions.ExchangeException;
+import org.knowm.xchange.exceptions.NotAvailableFromExchangeException;
+import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException;
+import org.knowm.xchange.service.trade.TradeService;
+import org.knowm.xchange.service.trade.params.CancelOrderParams;
+import org.knowm.xchange.service.trade.params.TradeHistoryParams;
+import org.knowm.xchange.service.trade.params.orders.OpenOrdersParams;
+
+import java.io.IOException;
+
+public final class CoinbaseTradeServiceCDP extends CoinbaseTradeServiceRawCDP implements TradeService {
+
+ public CoinbaseTradeServiceCDP(Exchange exchange) {
+ super(exchange);
+ }
+
+ /**
+ * ********************************************************************************************************************************************************
+ */
+ @Override
+ public OpenOrders getOpenOrders() throws NotAvailableFromExchangeException, IOException {
+ return getOpenOrders(createOpenOrdersParams());
+ }
+
+ @Override
+ public OpenOrders getOpenOrders(OpenOrdersParams params)
+ throws ExchangeException, NotAvailableFromExchangeException,
+ NotYetImplementedForExchangeException, IOException {
+ throw new NotAvailableFromExchangeException();
+ }
+
+ @Override
+ public String placeLimitOrder(LimitOrder limitOrder) throws NotAvailableFromExchangeException {
+
+ throw new NotAvailableFromExchangeException();
+ }
+
+ @Override
+ public String placeStopOrder(StopOrder stopOrder) throws IOException {
+ throw new NotAvailableFromExchangeException();
+ }
+
+ @Override
+ public boolean cancelOrder(String orderId) throws NotAvailableFromExchangeException {
+
+ throw new NotAvailableFromExchangeException();
+ }
+
+ @Override
+ public boolean cancelOrder(CancelOrderParams orderParams)
+ throws ExchangeException, NotAvailableFromExchangeException,
+ NotYetImplementedForExchangeException, IOException {
+ throw new NotAvailableFromExchangeException();
+ }
+
+ @Override
+ public TradeHistoryParams createTradeHistoryParams() {
+ return new CoinbaseTradeHistoryParams();
+ }
+
+ @Override
+ public OpenOrdersParams createOpenOrdersParams() {
+ return null;
+ }
+
+ /**
+ * The Coinbase is not typical exchange. It has splitted buys and sells into wallets (accounts).
+ * To get it is necessary to know the accountId (wallet ID) see {@link AccountInfo#getWallets()}
+ */
+ public UserTrades getBuyTradeHistory(CoinbaseTradeHistoryParams params, String accountId)
+ throws IOException {
+ final CoinbaseBuySellResponse buys =
+ coinbase.getBuys(
+ signatureCreator2,
+ accountId,
+ params.getLimit(),
+ params.getStartId());
+ return CoinbaseAdapters.adaptTrades(buys.getData(), Order.OrderType.BID);
+ }
+
+ /**
+ * The Coinbase is not typical exchange. It has splitted buys and sells into wallets (accounts).
+ * To get it is necessary to know the accountId (wallet ID) from {@link AccountInfo#getWallets()}
+ */
+ public UserTrades getSellTradeHistory(CoinbaseTradeHistoryParams params, String accountId)
+ throws IOException {
+ final CoinbaseBuySellResponse sells =
+ coinbase.getSells(
+ signatureCreator2,
+ accountId,
+ params.getLimit(),
+ params.getStartId());
+ return CoinbaseAdapters.adaptTrades(sells.getData(), Order.OrderType.ASK);
+ }
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/service/CoinbaseTradeServiceRawCDP.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/service/CoinbaseTradeServiceRawCDP.java
new file mode 100644
index 00000000000..1598cb85840
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/cdp/service/CoinbaseTradeServiceRawCDP.java
@@ -0,0 +1,182 @@
+package org.knowm.xchange.coinbase.cdp.service;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.knowm.xchange.Exchange;
+import org.knowm.xchange.coinbase.v2.service.CoinbaseTradeHistoryParams;
+import org.knowm.xchange.coinbase.v2.Coinbase;
+import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseBuyData.CoinbaseBuy;
+import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseSellData.CoinbaseSell;
+import org.knowm.xchange.coinbase.v3.dto.transactions.CoinbaseAdvancedTradeAccountsResponse;
+import org.knowm.xchange.coinbase.v3.dto.transactions.CoinbaseAdvancedTradeFills;
+import org.knowm.xchange.coinbase.v3.dto.transactions.CoinbaseAdvancedTradeOrderFillsResponse;
+import org.knowm.xchange.currency.Currency;
+
+import javax.ws.rs.core.MediaType;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.List;
+
+class CoinbaseTradeServiceRawCDP extends CoinbaseBaseServiceCDP {
+
+ protected CoinbaseTradeServiceRawCDP(Exchange exchange) {
+ super(exchange);
+ }
+
+ /**
+ * Authenticated resource that lets you purchase Bitcoin using the primary bank account that is
+ * linked to your account. (You must link and verify your bank account through the website before
+ * this API call will work). The underlying optional parameter agree_btc_amount_varies is set to
+ * false.
+ *
+ * @see developers.coinbase.com/api/v2#place-buy-order
+ */
+ public CoinbaseBuy buy(String accountId, BigDecimal total, Currency currency, boolean commit)
+ throws IOException {
+
+ String path = "/v2/accounts/" + accountId + "/buys";
+ String apiKey = exchange.getExchangeSpecification().getApiKey();
+ BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch();
+ BuyPayload payload = new BuyPayload(total, currency.getCurrencyCode(), commit, false);
+ String body = new ObjectMapper().writeValueAsString(payload);
+ String signature = getSignature(timestamp, HttpMethod.POST, path, body);
+
+ showCurl(HttpMethod.POST, apiKey, timestamp, signature, path, body);
+
+ return coinbase
+ .buy(
+ MediaType.APPLICATION_JSON,
+ Coinbase.CB_VERSION_VALUE,
+ apiKey,
+ signature,
+ timestamp,
+ accountId,
+ payload)
+ .getData();
+ }
+
+ /**
+ * Authenticated resource that lets you convert Bitcoin crediting your primary bank account on
+ * Coinbase. (You must link and verify your bank account through the website before this API call
+ * will work).
+ *
+ * @see developers.coinbase.com/api/v2#place-sell-order
+ */
+ public CoinbaseSell sell(String accountId, BigDecimal total, Currency currency, boolean commit)
+ throws IOException {
+
+ return sellInternal(
+ accountId, new SellPayload(total, currency.getCurrencyCode(), commit, false));
+ }
+
+ /**
+ *
+ * @param params
+ * @return
+ * @throws IOException
+ */
+ public List getAdvancedTradeOrderFills(CoinbaseTradeHistoryParams params)
+ throws IOException {
+ final String apiKey = exchange.getExchangeSpecification().getApiKey();
+ final BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch();
+ String start = params.getStartDatetime().toString();
+ String end = params.getEndDateTime().toString();
+ return coinbaseV3.getFills(
+ signatureCreator2, null, null, start, end, params.getLimit(), params.getCursor())
+ .getFills();
+ }
+
+ /**
+ *
+ * @param params
+ * @return
+ * @throws IOException
+ */
+ public CoinbaseAdvancedTradeOrderFillsResponse getAdvancedTradeOrderFillsRow(CoinbaseTradeHistoryParams params)
+ throws IOException {
+ String start = params.getStartDatetime().toString();
+ String end = params.getEndDateTime().toString();
+ return coinbaseV3.getFills(
+ signatureCreator2, null, null, start, end, params.getLimit(), params.getCursor());
+ }
+
+ /**
+ *
+ * @return
+ * @throws IOException
+ */
+ public CoinbaseAdvancedTradeAccountsResponse getAdvancedTradeAccounts() throws IOException {
+ final String apiKey = exchange.getExchangeSpecification().getApiKey();
+ final BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch();
+ return coinbaseV3.getAccounts(
+ signatureCreator2, null, null);
+ }
+
+ /**
+ * Authenticated resource that lets you convert Bitcoin crediting your primary bank account on
+ * Coinbase. (You must link and verify your bank account through the website before this API call
+ * will work).
+ *
+ * @see developers.coinbase.com/api/v2#place-sell-order
+ */
+ public CoinbaseSell quote(String accountId, BigDecimal total, Currency currency)
+ throws IOException {
+
+ return sellInternal(accountId, new SellPayload(total, currency.getCurrencyCode(), false, true));
+ }
+
+ private CoinbaseSell sellInternal(String accountId, SellPayload payload) throws IOException {
+
+ String path = "/v2/accounts/" + accountId + "/sells";
+ String apiKey = exchange.getExchangeSpecification().getApiKey();
+ BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch();
+ String body = new ObjectMapper().writeValueAsString(payload);
+ String signature = getSignature(timestamp, HttpMethod.POST, path, body);
+
+ showCurl(HttpMethod.POST, apiKey, timestamp, signature, path, body);
+
+ return coinbase
+ .sell(
+ MediaType.APPLICATION_JSON,
+ Coinbase.CB_VERSION_VALUE,
+ apiKey,
+ signature,
+ timestamp,
+ accountId,
+ payload)
+ .getData();
+ }
+
+ class BuyPayload extends AbstractPayload {
+ @JsonProperty String total;
+
+ BuyPayload(BigDecimal total, String currency, boolean commit, boolean quote) {
+ super(currency, commit, quote);
+ this.total = total.toString();
+ }
+ }
+
+ class SellPayload extends AbstractPayload {
+ @JsonProperty String amount;
+
+ SellPayload(BigDecimal amount, String currency, boolean commit, boolean quote) {
+ super(currency, commit, quote);
+ this.amount = amount.toString();
+ }
+ }
+
+ abstract class AbstractPayload {
+ @JsonProperty String currency;
+ @JsonProperty boolean commit;
+ @JsonProperty boolean quote;
+
+ AbstractPayload(String currency, boolean commit, boolean quote) {
+ this.currency = currency;
+ this.commit = commit;
+ this.quote = quote;
+ }
+ }
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseAuthenticated.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseAuthenticated.java
index a26857e17b4..74bcd032df8 100644
--- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseAuthenticated.java
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseAuthenticated.java
@@ -11,11 +11,13 @@
import jakarta.ws.rs.core.MediaType;
import java.io.IOException;
import java.math.BigDecimal;
+import java.util.ArrayList;
import java.util.Map;
import org.knowm.xchange.coinbase.v2.dto.CoinbaseException;
import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseAccountData;
import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseAccountsData;
import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseBuyData;
+import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseExpandTransactionsResponse;
import org.knowm.xchange.coinbase.v2.dto.account.CoinbasePaymentMethodsData;
import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseSellData;
import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseTransactionsResponse;
@@ -60,6 +62,19 @@ CoinbaseTransactionsResponse getTransactions(
@PathParam("accountId") String accountId)
throws IOException, CoinbaseException;
+ @GET
+ @Path("accounts/{accountId}/transactions")
+ CoinbaseExpandTransactionsResponse getExpandedTransactions(
+ @HeaderParam(CB_VERSION) String apiVersion,
+ @HeaderParam(CB_ACCESS_KEY) String apiKey,
+ @HeaderParam(CB_ACCESS_SIGN) ParamsDigest signature,
+ @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp,
+ @PathParam("accountId") String accountId,
+ @QueryParam("limit") int limit,
+ @QueryParam("order") String orderType,
+ @QueryParam("starting_after") String startingFrom)
+ throws IOException, CoinbaseException;
+
@GET
@Path("accounts/{accountId}/buys")
CoinbaseBuySellResponse getBuys(
@@ -84,6 +99,30 @@ CoinbaseBuySellResponse getSells(
@QueryParam("starting_after") String startingAfter)
throws IOException, CoinbaseException;
+ @GET
+ @Path("accounts/{accountId}/deposits")
+ CoinbaseBuySellResponse getAllDeposits(
+ @HeaderParam(CB_VERSION) String apiVersion,
+ @HeaderParam(CB_ACCESS_KEY) String apiKey,
+ @HeaderParam(CB_ACCESS_SIGN) CoinbaseV2Digest signature,
+ @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp,
+ @PathParam("accountId") String accountId,
+ @QueryParam("limit") Integer limit,
+ @QueryParam("starting_after") String startingAfter)
+ throws IOException, CoinbaseException;
+
+ @GET
+ @Path("accounts/{accountId}/withdrawals")
+ CoinbaseBuySellResponse getAllWithdrawals(
+ @HeaderParam(CB_VERSION) String apiVersion,
+ @HeaderParam(CB_ACCESS_KEY) String apiKey,
+ @HeaderParam(CB_ACCESS_SIGN) CoinbaseV2Digest signature,
+ @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp,
+ @PathParam("accountId") String accountId,
+ @QueryParam("limit") Integer limit,
+ @QueryParam("starting_after") String startingAfter)
+ throws IOException, CoinbaseException;
+
@GET
@Path("accounts/{accountId}/deposits")
Map getDeposits(
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseExchange.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseExchange.java
index 7c95f4e19bd..5ea465d82a1 100644
--- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseExchange.java
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseExchange.java
@@ -6,14 +6,21 @@
import org.knowm.xchange.coinbase.v2.service.CoinbaseAccountService;
import org.knowm.xchange.coinbase.v2.service.CoinbaseMarketDataService;
import org.knowm.xchange.coinbase.v2.service.CoinbaseTradeService;
+import org.knowm.xchange.coinbase.cdp.service.CoinbaseAccountServiceCDP;
+import org.knowm.xchange.coinbase.cdp.service.CoinbaseTradeServiceCDP;
public class CoinbaseExchange extends BaseExchange implements Exchange {
@Override
protected void initServices() {
this.marketDataService = new CoinbaseMarketDataService(this);
- this.accountService = new CoinbaseAccountService(this);
- this.tradeService = new CoinbaseTradeService(this);
+ if (exchangeSpecification.getApiKey().startsWith("organizations")){
+ this.accountService = new CoinbaseAccountServiceCDP(this);
+ this.tradeService = new CoinbaseTradeServiceCDP(this);
+ } else {
+ this.accountService = new CoinbaseAccountService(this);
+ this.tradeService = new CoinbaseTradeService(this);
+ }
}
@Override
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseV2Digest.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseV2Digest.java
index fe7cbede7fb..cd7262e0f5c 100644
--- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseV2Digest.java
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseV2Digest.java
@@ -1,14 +1,17 @@
package org.knowm.xchange.coinbase.v2;
-import static org.knowm.xchange.coinbase.v2.CoinbaseAuthenticated.CB_ACCESS_TIMESTAMP;
-
-import jakarta.ws.rs.HeaderParam;
import org.knowm.xchange.service.BaseParamsDigest;
import org.knowm.xchange.utils.DigestUtils;
import si.mazi.rescu.RestInvocation;
+import javax.ws.rs.HeaderParam;
+
+import static org.knowm.xchange.coinbase.v2.CoinbaseAuthenticated.CB_ACCESS_TIMESTAMP;
+
public class CoinbaseV2Digest extends BaseParamsDigest {
+ public static final String ADVANCED_TRADING_V3 = "api/v3/brokerage/";
+
private CoinbaseV2Digest(String secretKey) {
super(secretKey, HMAC_SHA_256);
}
@@ -19,12 +22,14 @@ public static CoinbaseV2Digest createInstance(String secretKey) {
@Override
public String digestParams(RestInvocation restInvocation) {
- final String pathWithQueryString =
- restInvocation.getInvocationUrl().replace(restInvocation.getBaseUrl(), "");
- final String timestamp =
- restInvocation.getParamValue(HeaderParam.class, CB_ACCESS_TIMESTAMP).toString();
- final String message = timestamp + restInvocation.getHttpMethod() + pathWithQueryString;
-
+ String path = restInvocation.getInvocationUrl();
+ final String timestamp = restInvocation.getParamValue(HeaderParam.class, CB_ACCESS_TIMESTAMP).toString();
+ if (path.contains(ADVANCED_TRADING_V3)) {
+ path = "/" + restInvocation.getPath();
+ } else {
+ path = path.replace(restInvocation.getBaseUrl(), "");
+ }
+ String message = timestamp + restInvocation.getHttpMethod() + path;
return DigestUtils.bytesToHex(getMac().doFinal(message.getBytes()));
}
}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/CoinbaseUnitPrice.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/CoinbaseUnitPrice.java
new file mode 100644
index 00000000000..c98908fee91
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/CoinbaseUnitPrice.java
@@ -0,0 +1,30 @@
+package org.knowm.xchange.coinbase.v2.dto;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.ToString;
+import org.knowm.xchange.currency.Currency;
+import org.knowm.xchange.utils.Assert;
+
+import java.math.BigDecimal;
+
+@ToString
+@Getter
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class CoinbaseUnitPrice {
+
+ private final Currency currency;
+ private final BigDecimal amount;
+ private final int scale;
+
+ @JsonCreator
+ public CoinbaseUnitPrice(
+ @JsonProperty("amount") BigDecimal amount, @JsonProperty("currency") String currency, @JsonProperty("scale") int scale) {
+ this.amount = amount;
+ this.currency = Currency.getInstance(currency);
+ this.scale = scale;
+ }
+
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/CoinbaseExpandTransactionsResponse.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/CoinbaseExpandTransactionsResponse.java
new file mode 100644
index 00000000000..7104fcf0a41
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/CoinbaseExpandTransactionsResponse.java
@@ -0,0 +1,20 @@
+package org.knowm.xchange.coinbase.v2.dto.account;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import org.knowm.xchange.coinbase.v2.dto.account.transactions.CoinbasePagination;
+import org.knowm.xchange.coinbase.v2.dto.account.transactions.CoinbaseShowTransactionV2;
+
+import java.util.List;
+
+@Getter
+public class CoinbaseExpandTransactionsResponse {
+ private final List data;
+ private final CoinbasePagination pagination;
+
+ public CoinbaseExpandTransactionsResponse(@JsonProperty("pagination") CoinbasePagination pagination,
+ @JsonProperty("data") List data) {
+ this.data = data;
+ this.pagination = pagination;
+ }
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseAdvancedTradeFill.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseAdvancedTradeFill.java
new file mode 100644
index 00000000000..ddee60c8f33
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseAdvancedTradeFill.java
@@ -0,0 +1,38 @@
+package org.knowm.xchange.coinbase.v2.dto.account.transactions;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+
+@Getter
+public class CoinbaseAdvancedTradeFill {
+
+ private final String fillPrice;
+ private final String productId;
+ private final String orderId;
+ private final String commission;
+ private final String orderSide;
+
+ public CoinbaseAdvancedTradeFill(
+ @JsonProperty("fill_price") String fillPrice,
+ @JsonProperty("product_id") String productId,
+ @JsonProperty("order_id") String orderId,
+ @JsonProperty("commission") String commission,
+ @JsonProperty("order_side") String orderSide) {
+ this.fillPrice = fillPrice;
+ this.productId = productId;
+ this.orderId = orderId;
+ this.commission = commission;
+ this.orderSide = orderSide;
+ }
+
+ @Override
+ public String toString() {
+ return "{\"CoinbaseAdvancedTradeFill\":{"
+ + "\"fillPrice\":\"" + fillPrice + "\""
+ + ", \"productId\":\"" + productId + "\""
+ + ", \"orderId\":\"" + orderId + "\""
+ + ", \"commission\":\"" + commission + "\""
+ + ", \"orderSide\":\"" + orderSide + "\""
+ + "}}";
+ }
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseDepositWithdrawalResponse.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseDepositWithdrawalResponse.java
new file mode 100644
index 00000000000..3e5f47d60b7
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseDepositWithdrawalResponse.java
@@ -0,0 +1,14 @@
+package org.knowm.xchange.coinbase.v2.dto.account.transactions;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.List;
+import lombok.Getter;
+
+@Getter
+public class CoinbaseDepositWithdrawalResponse {
+ private final List data;
+
+ public CoinbaseDepositWithdrawalResponse(@JsonProperty("data") List data) {
+ this.data = data;
+ }
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbasePagination.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbasePagination.java
new file mode 100644
index 00000000000..47dd0d023ba
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbasePagination.java
@@ -0,0 +1,34 @@
+package org.knowm.xchange.coinbase.v2.dto.account.transactions;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.ToString;
+
+@ToString
+@Getter
+public class CoinbasePagination {
+
+
+ private final String endingBefore;
+ private final String startingAfter;
+ private final int limit;
+ private final String order;
+ private final String previousUri;
+ private final String nextUri;
+
+ public CoinbasePagination(
+ @JsonProperty("ending_before") String endingBefore,
+ @JsonProperty("starting_after") String startingAfter,
+ @JsonProperty("limit") int limit,
+ @JsonProperty("order") String order,
+ @JsonProperty("previous_uri") String previousUri,
+ @JsonProperty("next_uri") String nextUri
+ ) {
+ this.endingBefore = endingBefore;
+ this.startingAfter = startingAfter;
+ this.limit = limit;
+ this.order = order;
+ this.previousUri = previousUri;
+ this.nextUri = nextUri;
+ }
+}
\ No newline at end of file
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseShowTransactionV2.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseShowTransactionV2.java
new file mode 100644
index 00000000000..b5c32365937
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseShowTransactionV2.java
@@ -0,0 +1,71 @@
+package org.knowm.xchange.coinbase.v2.dto.account.transactions;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.ToString;
+import org.knowm.xchange.coinbase.v2.dto.CoinbaseAmount;
+
+@ToString
+@Getter
+public class CoinbaseShowTransactionV2 {
+ private final String id;
+ private final String type;
+ private final String status;
+ private final CoinbaseAmount amount;
+ private final CoinbaseAmount nativeAmount;
+ private final String description;
+ private final String createdAt;
+ private final String resource;
+ private final String resourcePath;
+ private final CoinbaseAdvancedTradeFill advancedTradeFill;
+ private final CoinbaseTransactionV2NetworkField network;
+ private final CoinbaseTransactionV2ToField to;
+ private final CoinbaseTransactionV2Field from;
+ private final boolean cancelable;
+ private final String idem;
+ private final CoinbaseTransactionV2Expand buy;
+ private final CoinbaseTransactionV2Expand sell;
+ private final CoinbaseTrade trade;
+
+ public CoinbaseShowTransactionV2(
+ @JsonProperty("id") String id,
+ @JsonProperty("type") String type,
+ @JsonProperty("status") String status,
+ @JsonProperty("amount") CoinbaseAmount amount,
+ @JsonProperty("native_amount") CoinbaseAmount nativeAmount,
+ @JsonProperty("description") String description,
+ @JsonProperty("created_at") String createdAt,
+ @JsonProperty("resource") String resource,
+ @JsonProperty("resource_path") String resourcePath,
+ @JsonProperty("advanced_trade_fill") CoinbaseAdvancedTradeFill advancedTradeFill,
+ @JsonProperty("network") CoinbaseTransactionV2NetworkField network,
+ @JsonProperty("to") CoinbaseTransactionV2ToField to,
+ @JsonProperty("from") CoinbaseTransactionV2Field from,
+ @JsonProperty("cancelable") boolean cancelable,
+ @JsonProperty("idem") String idem,
+ @JsonProperty("buy") CoinbaseTransactionV2Expand buy,
+ @JsonProperty("sell") CoinbaseTransactionV2Expand sell,
+ @JsonProperty("trade") CoinbaseTrade trade
+ )
+ {
+ this.id = id;
+ this.type = type;
+ this.status = status;
+ this.amount = amount;
+ this.nativeAmount = nativeAmount;
+ this.description = description;
+ this.createdAt = createdAt;
+ this.resource = resource;
+ this.resourcePath = resourcePath;
+ this.advancedTradeFill = advancedTradeFill;
+ this.network = network;
+ this.to = to;
+ this.from = from;
+ this.idem = idem;
+ this.buy = buy;
+ this.sell = sell;
+ this.trade = trade;
+ this.cancelable = cancelable;
+ }
+
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseTrade.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseTrade.java
new file mode 100644
index 00000000000..75c9bd56270
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseTrade.java
@@ -0,0 +1,46 @@
+package org.knowm.xchange.coinbase.v2.dto.account.transactions;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+
+@Getter
+public class CoinbaseTrade {
+
+ private final String id;
+ private final String paymentMethodName;
+ private final String resource;
+ private final String resourcePath;
+
+ public CoinbaseTrade(
+ @JsonProperty("id") String id,
+ @JsonProperty("payment_method_name") String paymentMethodName,
+ @JsonProperty("resource") String resource,
+ @JsonProperty("resource_path") String resourcePath) {
+ this.id = id;
+ this.paymentMethodName = paymentMethodName;
+ this.resource = resource;
+ this.resourcePath = resourcePath;
+ }
+
+ @Override
+ public String toString() {
+ return "{"
+ + "\"id\":"
+ + '\"'
+ + id
+ + '\"'
+ + ",\"paymentMethodName\":"
+ + '\"'
+ + paymentMethodName
+ + '\"'
+ + ",\"resource\":"
+ + '\"'
+ + resource
+ + '\"'
+ + ",\"resourcePath\":"
+ + '\"'
+ + resourcePath
+ + '\"'
+ + '}';
+ }
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseTransactionDetails.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseTransactionDetails.java
new file mode 100644
index 00000000000..fc2de0462eb
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseTransactionDetails.java
@@ -0,0 +1,38 @@
+package org.knowm.xchange.coinbase.v2.dto.account.transactions;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+
+@Getter
+public class CoinbaseTransactionDetails {
+ private final String title;
+ private final String subtitle;
+ private final String paymentMethodName;
+
+ public CoinbaseTransactionDetails(
+ @JsonProperty("title") String title,
+ @JsonProperty("subtitle") String subtitle,
+ @JsonProperty("payment_method_name") String paymentMethodName) {
+ this.title = title;
+ this.subtitle = subtitle;
+ this.paymentMethodName = paymentMethodName;
+ }
+
+ @Override
+ public String toString() {
+ return "{"
+ + "\"title\":"
+ + '\"'
+ + title
+ + '\"'
+ + ",\"subtitle\":"
+ + '\"'
+ + subtitle
+ + '\"'
+ + ",\"paymentMethodName\":"
+ + '\"'
+ + paymentMethodName
+ + '\"'
+ + "}";
+ }
+}
\ No newline at end of file
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseTransactionV2Expand.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseTransactionV2Expand.java
new file mode 100644
index 00000000000..d4164a9bcc6
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseTransactionV2Expand.java
@@ -0,0 +1,47 @@
+package org.knowm.xchange.coinbase.v2.dto.account.transactions;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.experimental.FieldDefaults;
+import org.knowm.xchange.coinbase.v2.dto.CoinbaseAmount;
+
+import static lombok.AccessLevel.PROTECTED;
+
+@Getter
+@FieldDefaults(level = PROTECTED)
+public class CoinbaseTransactionV2Expand {
+
+ private final String id;
+ private final String paymentMethodName;
+ private final CoinbaseAmount fee;
+ private final CoinbaseAmount total;
+ private final CoinbaseAmount subtotal;
+ private final String idem;
+
+ public CoinbaseTransactionV2Expand(
+ @JsonProperty("id") String id,
+ @JsonProperty("payment_method_name") String paymentMethodName,
+ @JsonProperty("fee") CoinbaseAmount fee,
+ @JsonProperty("total") CoinbaseAmount total,
+ @JsonProperty("subtotal") CoinbaseAmount subtotal,
+ @JsonProperty("idem") String idem) {
+ this.id = id;
+ this.paymentMethodName = paymentMethodName;
+ this.fee = fee;
+ this.total = total;
+ this.subtotal = subtotal;
+ this.idem = idem;
+ }
+
+ @Override
+ public String toString() {
+ return "{\"CoinbaseTransactionV2Expand\":{"
+ + "\"id\":\"" + id + "\""
+ + ", \"paymentMethodName\":\"" + paymentMethodName + "\""
+ + ", \"fee\":" + fee
+ + ", \"total\":" + total
+ + ", \"subtotal\":" + subtotal
+ + ", \"idem\":\"" + idem + "\""
+ + "}}";
+ }
+}
\ No newline at end of file
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseTransactionV2NetworkField.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseTransactionV2NetworkField.java
index 0aa284dbd5a..95624ac56f2 100644
--- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseTransactionV2NetworkField.java
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/account/transactions/CoinbaseTransactionV2NetworkField.java
@@ -2,14 +2,26 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
+import org.knowm.xchange.coinbase.v2.dto.CoinbaseAmount;
@Getter
public class CoinbaseTransactionV2NetworkField {
- private String status;
+ private final String status;
+ private final String networkName;
+ private final CoinbaseAmount transactionFee;
+ private final String hash;
- public CoinbaseTransactionV2NetworkField(@JsonProperty("status") String status) {
+ public CoinbaseTransactionV2NetworkField(
+ @JsonProperty("status") String status,
+ @JsonProperty("network_name") String networkName,
+ @JsonProperty("transaction_fee") CoinbaseAmount transactionFee,
+ @JsonProperty("hash") String hash
+ ) {
this.status = status;
+ this.networkName = networkName;
+ this.transactionFee = transactionFee;
+ this.hash = hash;
}
@Override
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseAccountService.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseAccountService.java
index 468b318f8e8..3a5ef41f9c5 100644
--- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseAccountService.java
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseAccountService.java
@@ -1,15 +1,15 @@
package org.knowm.xchange.coinbase.v2.service;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
import org.knowm.xchange.Exchange;
+import org.knowm.xchange.coinbase.CoinbaseAdapters;
+import org.knowm.xchange.coinbase.v2.Coinbase;
import org.knowm.xchange.coinbase.v2.dto.CoinbaseAmount;
import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseAccountData;
+import org.knowm.xchange.coinbase.v2.dto.account.transactions.CoinbaseBuySellResponse;
import org.knowm.xchange.currency.Currency;
import org.knowm.xchange.dto.account.AccountInfo;
import org.knowm.xchange.dto.account.Balance;
+import org.knowm.xchange.dto.account.FundingRecord;
import org.knowm.xchange.dto.account.Wallet;
import org.knowm.xchange.exceptions.ExchangeException;
import org.knowm.xchange.exceptions.NotAvailableFromExchangeException;
@@ -19,6 +19,12 @@
import org.knowm.xchange.service.trade.params.TradeHistoryParams;
import org.knowm.xchange.service.trade.params.WithdrawFundsParams;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
public final class CoinbaseAccountService extends CoinbaseAccountServiceRaw
implements AccountService {
@@ -63,6 +69,48 @@ public String withdrawFunds(WithdrawFundsParams params)
@Override
public TradeHistoryParams createFundingHistoryParams() {
- throw new NotAvailableFromExchangeException();
+ return new CoinbaseTradeHistoryParams();
+ }
+
+ /**
+ * The Coinbase is not typical exchange. It has splitted buys and sells into wallets (accounts).
+ * To get it is necessary to know the accountId (wallet ID) see {@link AccountInfo#getWallets()}
+ */
+ public List getWithdrawalHistory(CoinbaseTradeHistoryParams params, String accountId)
+ throws IOException {
+ final String apiKey = exchange.getExchangeSpecification().getApiKey();
+ final BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch();
+ final CoinbaseBuySellResponse withdrawals =
+ coinbase.getAllWithdrawals(
+ Coinbase.CB_VERSION_VALUE,
+ apiKey,
+ signatureCreator2,
+ timestamp,
+ accountId,
+ params.getLimit(),
+ params.getStartId()
+ );
+ return CoinbaseAdapters.adaptFundings(withdrawals.getData());
+ }
+
+ /**
+ * The Coinbase is not typical exchange. It has splitted buys and sells into wallets (accounts).
+ * To get it is necessary to know the accountId (wallet ID) from {@link AccountInfo#getWallets()}
+ */
+ public List getDepositHistory(CoinbaseTradeHistoryParams params, String accountId)
+ throws IOException {
+ final String apiKey = exchange.getExchangeSpecification().getApiKey();
+ final BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch();
+ final CoinbaseBuySellResponse deposits =
+ coinbase.getAllDeposits(
+ Coinbase.CB_VERSION_VALUE,
+ apiKey,
+ signatureCreator2,
+ timestamp,
+ accountId,
+ params.getLimit(),
+ params.getStartId()
+ );
+ return CoinbaseAdapters.adaptFundings(deposits.getData());
}
}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseAccountServiceRaw.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseAccountServiceRaw.java
index c16660ba2ea..c2f71312ef1 100644
--- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseAccountServiceRaw.java
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseAccountServiceRaw.java
@@ -2,20 +2,22 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
-import jakarta.ws.rs.core.MediaType;
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
import org.knowm.xchange.Exchange;
import org.knowm.xchange.coinbase.v2.Coinbase;
import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseAccountData.CoinbaseAccount;
+import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseExpandTransactionsResponse;
import org.knowm.xchange.coinbase.v2.dto.account.CoinbasePaymentMethodsData.CoinbasePaymentMethod;
import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseTransactionsResponse;
import org.knowm.xchange.currency.Currency;
+import javax.ws.rs.core.MediaType;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
public class CoinbaseAccountServiceRaw extends CoinbaseBaseService {
public CoinbaseAccountServiceRaw(Exchange exchange) {
@@ -30,6 +32,21 @@ public CoinbaseTransactionsResponse getTransactions(String accountId) throws IOE
Coinbase.CB_VERSION_VALUE, apiKey, signatureCreator2, timestamp, accountId);
}
+ public CoinbaseExpandTransactionsResponse getExpandTransactions(String accountId, CoinbaseTradeHistoryParams params, String orderType)
+ throws IOException {
+
+ String apiKey = exchange.getExchangeSpecification().getApiKey();
+ BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch();
+
+ return coinbase.getExpandedTransactions(
+ Coinbase.CB_VERSION_VALUE, apiKey,
+ signatureCreator2, timestamp,
+ accountId,
+ params.getLimit(),
+ orderType,
+ params.getStartId());
+ }
+
public Map getDeposits(String accountId) throws IOException {
String apiKey = exchange.getExchangeSpecification().getApiKey();
BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch();
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseBaseService.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseBaseService.java
index 28b540fd5fd..6d921e1701f 100644
--- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseBaseService.java
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseBaseService.java
@@ -1,11 +1,5 @@
package org.knowm.xchange.coinbase.v2.service;
-import jakarta.ws.rs.core.MediaType;
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-import javax.crypto.Mac;
import org.knowm.xchange.Exchange;
import org.knowm.xchange.client.ExchangeRestProxyBuilder;
import org.knowm.xchange.coinbase.service.CoinbaseDigest;
@@ -14,13 +8,22 @@
import org.knowm.xchange.coinbase.v2.CoinbaseV2Digest;
import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseCurrencyData.CoinbaseCurrency;
import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseTimeData.CoinbaseTime;
+import org.knowm.xchange.coinbase.v3.CoinbaseAuthenticatedV3;
import org.knowm.xchange.service.BaseExchangeService;
import org.knowm.xchange.service.BaseService;
import org.knowm.xchange.utils.DigestUtils;
+import javax.crypto.Mac;
+import javax.ws.rs.core.MediaType;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
public class CoinbaseBaseService extends BaseExchangeService implements BaseService {
protected final CoinbaseAuthenticated coinbase;
+ protected final CoinbaseAuthenticatedV3 coinbaseV3;
protected final CoinbaseV2Digest signatureCreator2;
protected CoinbaseBaseService(Exchange exchange) {
@@ -31,6 +34,11 @@ protected CoinbaseBaseService(Exchange exchange) {
CoinbaseAuthenticated.class, exchange.getExchangeSpecification())
.build();
+ coinbaseV3 =
+ ExchangeRestProxyBuilder.forInterface(
+ CoinbaseAuthenticatedV3.class, exchange.getExchangeSpecification())
+ .build();
+
signatureCreator2 =
CoinbaseV2Digest.createInstance(exchange.getExchangeSpecification().getSecretKey());
}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseTradeHistoryParams.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseTradeHistoryParams.java
index 7153f3fb58c..55e1493d63f 100644
--- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseTradeHistoryParams.java
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseTradeHistoryParams.java
@@ -4,11 +4,16 @@
import org.knowm.xchange.service.trade.params.TradeHistoryParamLimit;
import org.knowm.xchange.service.trade.params.TradeHistoryParamsIdSpan;
+import java.time.Instant;
+
public class CoinbaseTradeHistoryParams
implements TradeHistoryParamsIdSpan, TradeHistoryParamLimit {
private String startId;
private Integer limit;
+ private Instant startDatetime;
+ private Instant endDateTime;
+ private String cursor;
@Override
public Integer getLimit() {
@@ -39,4 +44,28 @@ public String getEndId() {
public void setEndId(String endId) {
throw new NotAvailableFromExchangeException("Coinbase does not support ending transaction ID.");
}
+
+ public Instant getStartDatetime() {
+ return startDatetime;
+ }
+
+ public void setStartDatetime(Instant startDatetime) {
+ this.startDatetime = startDatetime;
+ }
+
+ public Instant getEndDateTime() {
+ return endDateTime;
+ }
+
+ public void setEndDateTime(Instant endDateTime) {
+ this.endDateTime = endDateTime;
+ }
+
+ public String getCursor() {
+ return cursor;
+ }
+
+ public void setCursor(String cursor) {
+ this.cursor = cursor;
+ }
}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseTradeServiceRaw.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseTradeServiceRaw.java
index b20a8f5f1eb..e162517f363 100644
--- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseTradeServiceRaw.java
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseTradeServiceRaw.java
@@ -2,15 +2,20 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
-import jakarta.ws.rs.core.MediaType;
-import java.io.IOException;
-import java.math.BigDecimal;
import org.knowm.xchange.Exchange;
import org.knowm.xchange.coinbase.v2.Coinbase;
import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseBuyData.CoinbaseBuy;
import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseSellData.CoinbaseSell;
+import org.knowm.xchange.coinbase.v3.dto.transactions.CoinbaseAdvancedTradeAccountsResponse;
+import org.knowm.xchange.coinbase.v3.dto.transactions.CoinbaseAdvancedTradeFills;
+import org.knowm.xchange.coinbase.v3.dto.transactions.CoinbaseAdvancedTradeOrderFillsResponse;
import org.knowm.xchange.currency.Currency;
+import javax.ws.rs.core.MediaType;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.List;
+
class CoinbaseTradeServiceRaw extends CoinbaseBaseService {
protected CoinbaseTradeServiceRaw(Exchange exchange) {
@@ -65,6 +70,52 @@ public CoinbaseSell sell(String accountId, BigDecimal total, Currency currency,
accountId, new SellPayload(total, currency.getCurrencyCode(), commit, false));
}
+ /**
+ *
+ * @param params
+ * @return
+ * @throws IOException
+ */
+ public List getAdvancedTradeOrderFills(CoinbaseTradeHistoryParams params)
+ throws IOException {
+ final String apiKey = exchange.getExchangeSpecification().getApiKey();
+ final BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch();
+ String start = params.getStartDatetime().toString();
+ String end = params.getEndDateTime().toString();
+ final CoinbaseAdvancedTradeOrderFillsResponse fills = coinbaseV3.getFills(Coinbase.CB_VERSION_VALUE, apiKey, signatureCreator2, timestamp, null
+ , null, start, end, params.getLimit(), params.getCursor());
+ return fills.getFills();
+ }
+
+ /**
+ *
+ * @param params
+ * @return
+ * @throws IOException
+ */
+ public CoinbaseAdvancedTradeOrderFillsResponse getAdvancedTradeOrderFillsRow(CoinbaseTradeHistoryParams params)
+ throws IOException {
+ final String apiKey = exchange.getExchangeSpecification().getApiKey();
+ final BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch();
+ String start = params.getStartDatetime().toString();
+ String end = params.getEndDateTime().toString();
+ return coinbaseV3.getFills(Coinbase.CB_VERSION_VALUE, apiKey, signatureCreator2,
+ timestamp, null, null, start, end, params.getLimit(), params.getCursor());
+ }
+
+ /**
+ *
+ * @return
+ * @throws IOException
+ */
+ public CoinbaseAdvancedTradeAccountsResponse getAdvancedTradeAccounts() throws IOException {
+ final String apiKey = exchange.getExchangeSpecification().getApiKey();
+ final BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch();
+ final CoinbaseAdvancedTradeAccountsResponse accounts = coinbaseV3.getAccounts(Coinbase.CB_VERSION_VALUE, apiKey, signatureCreator2,
+ timestamp, null, null);
+ return accounts;
+ }
+
/**
* Authenticated resource that lets you convert Bitcoin crediting your primary bank account on
* Coinbase. (You must link and verify your bank account through the website before this API call
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/TransactionType.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/TransactionType.java
new file mode 100644
index 00000000000..c96bc268f9f
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/TransactionType.java
@@ -0,0 +1,18 @@
+package org.knowm.xchange.coinbase.v2.service;
+
+public enum TransactionType {
+ BUY("buy"),
+ SELL("sell"),
+ RECEIVE("receive"),
+ SEND("send");
+
+ String name;
+
+ TransactionType(String name) {
+ this.name = name;
+ }
+
+ String getName() {
+ return name;
+ }
+}
\ No newline at end of file
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/CoinbaseAuthenticatedV3.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/CoinbaseAuthenticatedV3.java
new file mode 100644
index 00000000000..9fa07da4ba0
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/CoinbaseAuthenticatedV3.java
@@ -0,0 +1,74 @@
+package org.knowm.xchange.coinbase.v3;
+
+import org.knowm.xchange.coinbase.v2.Coinbase;
+import org.knowm.xchange.coinbase.v2.dto.CoinbaseException;
+import org.knowm.xchange.coinbase.v3.dto.transactions.CoinbaseAdvancedTradeAccountsResponse;
+import org.knowm.xchange.coinbase.v3.dto.transactions.CoinbaseAdvancedTradeOrderFillsResponse;
+import si.mazi.rescu.ParamsDigest;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import java.io.IOException;
+import java.math.BigDecimal;
+
+import static org.knowm.xchange.coinbase.v2.CoinbaseV2Digest.ADVANCED_TRADING_V3;
+
+@Path(ADVANCED_TRADING_V3)
+@Produces(MediaType.APPLICATION_JSON)
+public interface CoinbaseAuthenticatedV3 extends Coinbase {
+
+ /**
+ * All API key requests must be signed and contain the following headers.
+ *
+ * All request bodies should have content type application/json and be valid JSON.
+ *
+ *
The CB-ACCESS-SIGN header is generated by creating a sha256 HMAC using the secret key on the
+ * prehash string timestamp + method + requestPath + body (where + represents string
+ * concatenation). The timestamp value is the same as the CB-ACCESS-TIMESTAMP header.
+ *
+ *
The body is the request body string or omitted if there is no request body (typically for
+ * GET requests).
+ *
+ *
The method should be UPPER CASE.
+ *
+ *
developers.coinbase.com/api/v2#api-key
+ */
+ String CB_ACCESS_KEY = "CB-ACCESS-KEY";
+
+ String CB_ACCESS_SIGN = "CB-ACCESS-SIGN";
+ String CB_ACCESS_TIMESTAMP = "CB-ACCESS-TIMESTAMP";
+
+ // ok
+ @GET
+ @Path("accounts")
+ CoinbaseAdvancedTradeAccountsResponse getAccounts(
+ @HeaderParam(CB_VERSION) String apiVersion,
+ @HeaderParam(CB_ACCESS_KEY) String apiKey,
+ @HeaderParam(CB_ACCESS_SIGN) ParamsDigest signature,
+ @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp,
+ @QueryParam("limit") Integer limit,
+ @QueryParam("cursor") String cursor)
+ throws IOException, CoinbaseException;
+
+ @GET
+ @Path("orders/historical/fills")
+ CoinbaseAdvancedTradeOrderFillsResponse getFills(
+ @HeaderParam(CB_VERSION) String apiVersion,
+ @HeaderParam(CB_ACCESS_KEY) String apiKey,
+ @HeaderParam(CB_ACCESS_SIGN) ParamsDigest signature,
+ @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp,
+ @QueryParam("order_id") String orderId,
+ @QueryParam("product_id") String productId,
+ @QueryParam("start_sequence_timestamp") String start,
+ @QueryParam("end_sequence_timestamp") String end,
+ @QueryParam("limit") int limit,
+ @QueryParam("cursor") String cursor
+ )
+ throws IOException, CoinbaseException;
+
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/CoinbaseAuthenticatedV3CDP.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/CoinbaseAuthenticatedV3CDP.java
new file mode 100644
index 00000000000..930c11ca3a6
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/CoinbaseAuthenticatedV3CDP.java
@@ -0,0 +1,63 @@
+package org.knowm.xchange.coinbase.v3;
+
+import org.knowm.xchange.coinbase.v2.Coinbase;
+import org.knowm.xchange.coinbase.v2.dto.CoinbaseException;
+import org.knowm.xchange.coinbase.v3.dto.transactions.CoinbaseAdvancedTradeAccountsResponse;
+import org.knowm.xchange.coinbase.v3.dto.transactions.CoinbaseAdvancedTradeOrderFillsResponse;
+import si.mazi.rescu.ParamsDigest;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import java.io.IOException;
+
+import static org.knowm.xchange.coinbase.v2.CoinbaseV2Digest.ADVANCED_TRADING_V3;
+
+@Path(ADVANCED_TRADING_V3)
+@Produces(MediaType.APPLICATION_JSON)
+public interface CoinbaseAuthenticatedV3CDP extends Coinbase {
+
+ /**
+ * All API key requests must be signed and contain the following headers.
+ *
+ *
All request bodies should have content type application/json and be valid JSON.
+ *
+ *
The CB-ACCESS-SIGN header is generated by creating a sha256 HMAC using the secret key on the
+ * prehash string timestamp + method + requestPath + body (where + represents string
+ * concatenation). The timestamp value is the same as the CB-ACCESS-TIMESTAMP header.
+ *
+ *
The body is the request body string or omitted if there is no request body (typically for
+ * GET requests).
+ *
+ *
The method should be UPPER CASE.
+ *
+ *
developers.coinbase.com/api/v2#api-key
+ */
+
+ // ok
+ @GET
+ @Path("accounts")
+ CoinbaseAdvancedTradeAccountsResponse getAccounts(
+ @HeaderParam("Authorization") ParamsDigest signature,
+ @QueryParam("limit") Integer limit,
+ @QueryParam("cursor") String cursor)
+ throws IOException, CoinbaseException;
+
+ @GET
+ @Path("orders/historical/fills")
+ CoinbaseAdvancedTradeOrderFillsResponse getFills(
+ @HeaderParam("Authorization") ParamsDigest signature,
+ @QueryParam("order_id") String orderId,
+ @QueryParam("product_id") String productId,
+ @QueryParam("start_sequence_timestamp") String start,
+ @QueryParam("end_sequence_timestamp") String end,
+ @QueryParam("limit") int limit,
+ @QueryParam("cursor") String cursor
+ )
+ throws IOException, CoinbaseException;
+
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/dto/transactions/CoinbaseAdvancedTradeAccountsResponse.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/dto/transactions/CoinbaseAdvancedTradeAccountsResponse.java
new file mode 100644
index 00000000000..9ddd0c392c8
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/dto/transactions/CoinbaseAdvancedTradeAccountsResponse.java
@@ -0,0 +1,25 @@
+package org.knowm.xchange.coinbase.v3.dto.transactions;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+
+import java.util.List;
+
+@Getter
+public class CoinbaseAdvancedTradeAccountsResponse {
+ public final List accounts;
+ private final boolean hasNext;
+ private final String cursor;
+ private final String size;
+
+ public CoinbaseAdvancedTradeAccountsResponse(@JsonProperty("accounts") List accounts,
+ @JsonProperty(
+ "has_next") boolean hasNext, @JsonProperty("cursor") String cursor, @JsonProperty("size") String size
+ ) {
+ this.accounts = accounts;
+ this.hasNext = hasNext;
+ this.cursor = cursor;
+ this.size = size;
+ }
+
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/dto/transactions/CoinbaseAdvancedTradeFills.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/dto/transactions/CoinbaseAdvancedTradeFills.java
new file mode 100644
index 00000000000..41a2bac67ca
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/dto/transactions/CoinbaseAdvancedTradeFills.java
@@ -0,0 +1,61 @@
+package org.knowm.xchange.coinbase.v3.dto.transactions;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.experimental.FieldDefaults;
+
+import java.math.BigDecimal;
+
+import static lombok.AccessLevel.PRIVATE;
+
+@Getter
+@FieldDefaults(level = PRIVATE, makeFinal = true)
+public class CoinbaseAdvancedTradeFills {
+
+ String entryId;
+ String tradeId;
+ String orderId;
+ String tradeTime;
+ String tradeType;
+ BigDecimal price;
+ BigDecimal size;
+ BigDecimal commission;
+ String productId;
+ String sequenceTimestamp;
+ String liquidityIndicator;
+ String sizeInQuote;
+ String userId;
+ String side;
+
+ public CoinbaseAdvancedTradeFills(
+ @JsonProperty("entry_id") String entryId,
+ @JsonProperty("trade_id") String tradeId,
+ @JsonProperty("order_id") String orderId,
+ @JsonProperty("trade_time") String tradeTime,
+ @JsonProperty("trade_type") String tradeType,
+ @JsonProperty("price") BigDecimal price,
+ @JsonProperty("size") BigDecimal size,
+ @JsonProperty("commission") BigDecimal commission,
+ @JsonProperty("product_id") String productId,
+ @JsonProperty("sequence_timestamp") String sequenceTimestamp,
+ @JsonProperty("liquidity_indicator") String liquidityIndicator,
+ @JsonProperty("size_in_quote") String sizeInQuote,
+ @JsonProperty("user_id") String userId,
+ @JsonProperty("side") String side) {
+
+ this.entryId = entryId;
+ this.tradeId = tradeId;
+ this.orderId = orderId;
+ this.tradeTime = tradeTime;
+ this.tradeType = tradeType;
+ this.price = price;
+ this.size = size;
+ this.commission = commission;
+ this.productId = productId;
+ this.sequenceTimestamp = sequenceTimestamp;
+ this.liquidityIndicator = liquidityIndicator;
+ this.sizeInQuote = sizeInQuote;
+ this.userId = userId;
+ this.side = side;
+ }
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/dto/transactions/CoinbaseAdvancedTradeOrderFillsResponse.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/dto/transactions/CoinbaseAdvancedTradeOrderFillsResponse.java
new file mode 100644
index 00000000000..eb5d6a58d57
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/dto/transactions/CoinbaseAdvancedTradeOrderFillsResponse.java
@@ -0,0 +1,19 @@
+package org.knowm.xchange.coinbase.v3.dto.transactions;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+
+import java.util.List;
+
+@Getter
+public class CoinbaseAdvancedTradeOrderFillsResponse {
+ private final List fills;
+ private final String cursor;
+
+ public CoinbaseAdvancedTradeOrderFillsResponse(@JsonProperty("fills") List fills, @JsonProperty("cursor") String cursor ) {
+ this.fills = fills;
+ this.cursor = cursor;
+
+ }
+
+}
diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/dto/transactions/CoinbaseAdvancedTradesAccounts.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/dto/transactions/CoinbaseAdvancedTradesAccounts.java
new file mode 100644
index 00000000000..b803775389b
--- /dev/null
+++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v3/dto/transactions/CoinbaseAdvancedTradesAccounts.java
@@ -0,0 +1,54 @@
+package org.knowm.xchange.coinbase.v3.dto.transactions;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.experimental.FieldDefaults;
+
+import static lombok.AccessLevel.PRIVATE;
+
+@Getter
+@FieldDefaults(level = PRIVATE, makeFinal = true)
+public class CoinbaseAdvancedTradesAccounts {
+
+ String uuid;
+ String name;
+ String currency;
+ Object availableBalance;
+ boolean def;
+ boolean active;
+ String createdAt;
+ String updatedAt;
+ String deletedAt;
+ String type;
+ boolean ready;
+ Object hold;
+
+ public CoinbaseAdvancedTradesAccounts(
+ @JsonProperty("uuid") String uuid,
+ @JsonProperty("name") String name,
+ @JsonProperty("currency") String currency,
+ @JsonProperty("available_balance") Object availableBalance,
+ @JsonProperty("default") boolean def,
+ @JsonProperty("active") boolean active,
+ @JsonProperty("created_at") String createdAt,
+ @JsonProperty("updated_at") String updatedAt,
+ @JsonProperty("deleted_at") String deletedAt,
+ @JsonProperty("type") String type,
+ @JsonProperty("ready") boolean ready,
+ @JsonProperty("hold") Object hold
+ ) {
+
+ this.uuid = uuid;
+ this.name = name;
+ this.currency = currency;
+ this.availableBalance = availableBalance;
+ this.def = def;
+ this.active = active;
+ this.createdAt = createdAt;
+ this.updatedAt = updatedAt;
+ this.deletedAt = deletedAt;
+ this.type = type;
+ this.ready = ready;
+ this.hold = hold;
+ }
+}