Skip to content

Commit

Permalink
[resolves knowm#1205] Update Coinbase Api to use the new Coinbase Exc…
Browse files Browse the repository at this point in the history
…hange api V2
  • Loading branch information
tdiesler committed Dec 27, 2017
1 parent 364e73f commit 65b24e4
Show file tree
Hide file tree
Showing 63 changed files with 3,966 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ META-INF/

# externalized configuration file for keys
exchangeConfiguration.json
*secret.keys

# emacs
*~
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.knowm.xchange.coinbase.v2;

import java.io.IOException;

import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
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 org.knowm.xchange.coinbase.v2.dto.CoinbaseException;
import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseCurrencyData;
import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseExchangeRateData;
import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbasePriceData;
import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseTimeData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Path("/")
@Produces(MediaType.APPLICATION_JSON)
public interface Coinbase {

public static Logger LOG = LoggerFactory.getLogger(Coinbase.class.getPackage().getName());

/**
* All API calls should be made with a CB-VERSION header which guarantees that your call is using the correct API version.
* <a href="https://developers.coinbase.com/api/v2#versioning">developers.coinbase.com/api/v2#versioning</a>
*/
final String CB_VERSION = "CB-VERSION";
final String CB_VERSION_VALUE = "2017-11-26";

@GET
@Path("currencies")
CoinbaseCurrencyData getCurrencies(@HeaderParam(CB_VERSION) String apiVersion) throws IOException, CoinbaseException;

@GET
@Path("exchange-rates")
CoinbaseExchangeRateData getCurrencyExchangeRates(@HeaderParam(CB_VERSION) String apiVersion) throws IOException, CoinbaseException;

@GET
@Path("prices/{pair}/buy")
CoinbasePriceData getBuyPrice(@HeaderParam(CB_VERSION) String apiVersion, @PathParam("pair") String pair) throws IOException, CoinbaseException;

@GET
@Path("prices/{pair}/sell")
CoinbasePriceData getSellPrice(@HeaderParam(CB_VERSION) String apiVersion, @PathParam("pair") String pair) throws IOException, CoinbaseException;

@GET
@Path("prices/{pair}/spot")
CoinbasePriceData getSpotRate(@HeaderParam(CB_VERSION) String apiVersion, @PathParam("pair") String pair) throws IOException, CoinbaseException;

@GET
@Path("prices/{pair}/spot")
CoinbasePriceData getHistoricalSpotRate(@HeaderParam(CB_VERSION) String apiVersion, @PathParam("pair") String pair, @QueryParam("date") String date) throws IOException, CoinbaseException;

@GET
@Path("time")
CoinbaseTimeData getTime(@HeaderParam(CB_VERSION) String apiVersion) throws IOException, CoinbaseException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.knowm.xchange.coinbase.v2;

import java.io.IOException;
import java.math.BigDecimal;

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.core.MediaType;

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.CoinbasePaymentMethodsData;
import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseSellData;

@Path("/")
@Produces(MediaType.APPLICATION_JSON)
public interface CoinbaseAuthenticated 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.
*
* <a href="https://developers.coinbase.com/api/v2#api-key">developers.coinbase.com/api/v2#api-key</a>
*/
final String CB_ACCESS_KEY = "CB-ACCESS-KEY";
final String CB_ACCESS_SIGN = "CB-ACCESS-SIGN";
final String CB_ACCESS_TIMESTAMP = "CB-ACCESS-TIMESTAMP";

final String CONTENT_TYPE = "Content-Type";

@GET
@Path("accounts")
CoinbaseAccountsData getAccounts(@HeaderParam(CB_VERSION) String apiVersion, @HeaderParam(CB_ACCESS_KEY) String apiKey, @HeaderParam(CB_ACCESS_SIGN) String signature,
@HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp) throws IOException, CoinbaseException;

@GET
@Path("accounts/{currency}")
CoinbaseAccountData getAccount(@HeaderParam(CB_VERSION) String apiVersion, @HeaderParam(CB_ACCESS_KEY) String apiKey, @HeaderParam(CB_ACCESS_SIGN) String signature,
@HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp, @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(CB_VERSION) String apiVersion, @HeaderParam(CB_ACCESS_KEY) String apiKey,
@HeaderParam(CB_ACCESS_SIGN) String signature, @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp) 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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.knowm.xchange.coinbase.v2;

import org.knowm.xchange.BaseExchange;
import org.knowm.xchange.Exchange;
import org.knowm.xchange.ExchangeSpecification;
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.utils.nonce.CurrentTimeNonceFactory;

import si.mazi.rescu.SynchronizedValueFactory;

public class CoinbaseExchange extends BaseExchange implements Exchange {

private SynchronizedValueFactory<Long> nonceFactory = new CurrentTimeNonceFactory();

@Override
protected void initServices() {
this.marketDataService = new CoinbaseMarketDataService(this);
this.accountService = new CoinbaseAccountService(this);
this.tradeService = new CoinbaseTradeService(this);
}

@Override
public ExchangeSpecification getDefaultExchangeSpecification() {

final ExchangeSpecification exchangeSpecification = new ExchangeSpecification(this.getClass().getCanonicalName());
exchangeSpecification.setSslUri("https://api.coinbase.com/v2");
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;
}

@Override
public SynchronizedValueFactory<Long> getNonceFactory() {

return nonceFactory;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.knowm.xchange.coinbase.v2.dto;

import java.math.BigDecimal;

import org.knowm.xchange.utils.Assert;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class CoinbaseAmount {

private final String currency;
private final BigDecimal amount;
private final String toString;

@JsonCreator
public CoinbaseAmount(@JsonProperty("currency") String currency, @JsonProperty("amount") BigDecimal amount) {
Assert.notNull(currency, "Null currency");
Assert.notNull(amount, "Null amount");
this.currency = currency;
this.amount = amount;

toString = String.format("%.8f %s", amount, currency);
}

public String getCurrency() {
return currency;
}

public BigDecimal getAmount() {
return amount;
}

@Override
public int hashCode() {
return toString().hashCode();
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
CoinbaseAmount other = (CoinbaseAmount) obj;
return amount.compareTo(other.amount) == 0 && currency.equals(other.currency);
}

@Override
public String toString() {
return toString;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.knowm.xchange.coinbase.v2.dto;

import java.util.List;

import com.fasterxml.jackson.annotation.JsonProperty;

import si.mazi.rescu.HttpStatusExceptionSupport;

@SuppressWarnings("serial")
public class CoinbaseException extends HttpStatusExceptionSupport {

public CoinbaseException(@JsonProperty("errors") List<CoinbaseError> errors) {
super(errors.get(0).message);
}

static class CoinbaseError {

@JsonProperty
String id;
@JsonProperty
String message;

@Override
public String toString() {
return "CoinbaseError [id=" + id + ", message=" + message + "]";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.knowm.xchange.coinbase.v2.dto;

import java.math.BigDecimal;

import org.knowm.xchange.currency.Currency;
import org.knowm.xchange.utils.Assert;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class CoinbasePrice {

private final Currency currency;
private final BigDecimal amount;
private final String toString;

@JsonCreator
public CoinbasePrice(@JsonProperty("amount") BigDecimal amount, @JsonProperty("currency") String currency) {
this(amount, Currency.getInstance(currency));
}

public CoinbasePrice(BigDecimal amount, Currency currency) {
Assert.notNull(currency, "Null currency");
Assert.notNull(amount, "Null amount");
this.currency = currency;
this.amount = amount;

toString = String.format("%.2f %s", amount, currency);
}

public Currency getCurrency() {
return currency;
}

public BigDecimal getAmount() {
return amount;
}

@Override
public int hashCode() {
return toString().hashCode();
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
CoinbasePrice other = (CoinbasePrice) obj;
return amount.compareTo(other.amount) == 0 && currency.equals(other.currency);
}

@Override
public String toString() {
return toString;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.knowm.xchange.coinbase.v2.dto.account;

import org.knowm.xchange.coinbase.v2.dto.CoinbaseAmount;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class CoinbaseAccountData {

private final CoinbaseAccount data;

private CoinbaseAccountData(@JsonProperty("data") CoinbaseAccount data) {
this.data = data;
}

public CoinbaseAccount getData() {
return data;
}

@Override
public String toString() {
return "" + data;
}

@JsonIgnoreProperties(ignoreUnknown = true)
public static class CoinbaseAccount {

private final String id;
private final String name;
private final CoinbaseAmount balance;

@JsonCreator
CoinbaseAccount(@JsonProperty("id") String id, @JsonProperty("name") String name, @JsonProperty("balance") CoinbaseAmount balance) {
this.id = id;
this.name = name;
this.balance = balance;
}

public String getId() {
return id;
}

public String getName() {
return name;
}

public CoinbaseAmount getBalance() {
return balance;
}

@Override
public String toString() {
return "CoinbaseAccount [id=" + id + ", name=" + name + ", balance=" + balance + "]";
}
}
}
Loading

0 comments on commit 65b24e4

Please sign in to comment.