├── .github └── workflows │ └── build.yaml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── api ├── build.gradle └── src │ ├── main │ └── java │ │ └── com │ │ └── coinbase │ │ └── exchange │ │ └── api │ │ ├── accounts │ │ ├── Account.java │ │ ├── AccountHistory.java │ │ └── AccountService.java │ │ ├── deposits │ │ └── DepositService.java │ │ ├── exchange │ │ ├── CoinbaseExchange.java │ │ └── CoinbaseExchangeImpl.java │ │ ├── marketdata │ │ ├── MarketData.java │ │ ├── MarketDataService.java │ │ ├── Message.java │ │ ├── MessageEX.java │ │ ├── OrderItem.java │ │ └── Trade.java │ │ ├── orders │ │ ├── Order.java │ │ ├── OrderBuilder.java │ │ └── OrderService.java │ │ ├── payments │ │ ├── AccountLimit.java │ │ ├── Amount.java │ │ ├── BankCountry.java │ │ ├── CoinbaseAccount.java │ │ ├── DepositInformation.java │ │ ├── Limit.java │ │ ├── PaymentService.java │ │ ├── PaymentType.java │ │ └── SepaDepositInformation.java │ │ ├── products │ │ └── ProductService.java │ │ ├── reports │ │ ├── ReportRequest.java │ │ ├── ReportResponse.java │ │ ├── ReportService.java │ │ └── TimePeriod.java │ │ ├── transfers │ │ ├── Transfer.java │ │ └── TransferService.java │ │ ├── useraccount │ │ ├── UserAccountData.java │ │ └── UserAccountService.java │ │ └── withdrawals │ │ └── WithdrawalsService.java │ └── test │ ├── java │ └── com │ │ └── coinbase │ │ └── exchange │ │ └── api │ │ ├── BaseIntegrationTest.java │ │ ├── TestExchangeApplication.java │ │ ├── accounts │ │ ├── AccountsIntegrationTest.java │ │ ├── DepositIntegrationTest.java │ │ ├── UserAccountServiceIntegrationTest.java │ │ └── WithdrawalIntegrationTest.java │ │ ├── authentication │ │ └── AuthenticationIntegrationIntegrationTest.java │ │ ├── config │ │ └── IntegrationTestConfiguration.java │ │ ├── marketdata │ │ ├── MarketDataIntegrationTest.java │ │ └── OrderItemDeserializerTest.java │ │ ├── orders │ │ └── OrderIntegrationTest.java │ │ ├── payments │ │ └── PaymentIntegrationTest.java │ │ ├── products │ │ └── ProductsIntegrationTest.java │ │ └── transfers │ │ └── TransferServiceIntegrationTest.java │ └── resources │ └── application-test.yml ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── model ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── coinbase │ └── exchange │ └── model │ ├── Candle.java │ ├── Candles.java │ ├── CoinbasePaymentRequest.java │ ├── CryptoPaymentRequest.java │ ├── Currency.java │ ├── Detail.java │ ├── Fill.java │ ├── Granularity.java │ ├── Hold.java │ ├── MonetaryRequest.java │ ├── NewLimitOrderSingle.java │ ├── NewMarketOrderSingle.java │ ├── NewOrderSingle.java │ ├── PaymentRequest.java │ ├── PaymentResponse.java │ ├── Product.java │ └── ProductOrderBook.java ├── security ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── coinbase │ └── exchange │ └── security │ ├── Signature.java │ └── constants │ └── ExchangeConstants.java ├── settings.gradle └── websocketfeed ├── build.gradle └── src ├── main └── java │ └── com │ └── coinbase │ └── exchange │ └── websocketfeed │ ├── ActivateOrderBookMessage.java │ ├── ChangedOrderBookMessage.java │ ├── Channel.java │ ├── ChannelName.java │ ├── DoneOrderBookMessage.java │ ├── ErrorOrderBookMessage.java │ ├── FeedMessage.java │ ├── HeartBeat.java │ ├── L2UpdateMessage.java │ ├── MatchedOrderBookMessage.java │ ├── OpenedOrderBookMessage.java │ ├── OrderBookMessage.java │ ├── ReceivedOrderBookMessage.java │ ├── SnapshotMessage.java │ ├── StatusMessage.java │ ├── Subscribe.java │ ├── SubscriptionsMessage.java │ ├── TickerMessage.java │ ├── WebsocketFeed.java │ └── WebsocketMessageHandler.java └── test └── java └── com └── coinbase └── exchange └── websocketfeed ├── ActivateOrderBookMessageTest.java ├── ChangedOrderBookMessageTest.java ├── DoneOrderBookMessageTest.java ├── ErrorOrderBookMessageTest.java ├── HeartBeatTest.java ├── L2UpdateMessageTest.java ├── MatchedOrderBookMessageTest.java ├── OpenedOrderBookMessageTest.java ├── ReceivedOrderBookMessageTest.java ├── SnapshotMessageTest.java ├── StatusMessageTest.java └── TickerMessageTest.java /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: [push] 3 | 4 | jobs: 5 | byz-build: 6 | name: Coinbase Pro Build 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: set up JDK 11 | uses: actions/setup-java@v1 12 | with: 13 | java-version: 11 14 | - name: Build (non-master branch) 15 | if: github.ref != 'refs/heads/master' 16 | run: ./gradlew build -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea/ 3 | .gradle/ 4 | *.log 5 | **/classes/ 6 | **/build/ 7 | **/out/ 8 | **/application.*.yml -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | If you'd like to contribute you'd be more than welcome. The code base is ever growing and in order to make it usable by as many people as possible, we'd like your ideas, suggestions, feedback, as well as accepting your code improvements. 4 | 5 | Join us on the Gitter.im channel (chat link at the top of the main README.md file) 6 | 7 | If you'd like to contribute code, fork the repo, make your changes, then create a pull request back into the irufus/gdax-java codebase. Your code will then be reviewed prior to merging it in. 8 | 9 | Please ensure where possible, tests are included with any new features. Tests in this project are JUnit. Mockito, PowerMockito, etc. are all possible. The only real restriction is to Keep It Simple (where possible). 10 | 11 | Please also update/amend the README.md as necessary regarding changes so that it is kept up to date. 12 | 13 | # TESTING 14 | 15 | Tests act as documentation for the code, demonstrating actual usage. 16 | If you're not familiar with TDD then now is a great time to begin! 17 | Test Driven Development will: 18 | - help determine what to build 19 | - help you to prioritise which elements need to be in place first 20 | - help you to implement a solution using the minimal amount of code 21 | - frontload the stress of the design of your code, which should emerge from/be driven by the tests 22 | 23 | Not testing code leads to poor implementations that are hard to read, debug and are typically unmaintainable. 24 | 25 | If you'd like to contribute to the codebase, your code must be robust. To ensure its Robust you must provide tests that cover the scenarios your solution should handle. 26 | 27 | Currently tests follow a given/when/then approach. That is: 28 | - `given` some setup and preconditions 29 | - `when` I invoke method X on some object under test 30 | - `then` some results should be expected. 31 | 32 | You'll spot this pattern clearly in the test code because most of the precondition code is grouped together, then there's a separated line calling to some method on the testObject (typically), and finally a group of assertions at the end of the test. These three sections are typically grouped using new lines as separators. 33 | 34 | Test naming - typically tests are named in the following way: `shouldDoXWhenY`. 35 | 36 | If you spot a bug, write a test for it to highlight the issue, create a fix, and submit a pull request for review. 37 | 38 | Code that has very little in the way of testing is more likely to be rejected. 39 | 40 | The current codebase tests what is sensible and possible to test. Mainly getting things. 41 | 42 | ## I want to create some new element in the codebase 43 | 44 | Where do you start? 45 | 46 | Add this template, add the relevant tests you want, then begin implementing your code. 47 | 48 | Note Dependency injection is far more predictable in spring if constructor injection is used i.e. put the `@Autowired`/`@Inject` annotation on the constructor rather than a field in your class. 49 | If a class only has one constructor the `@Autowired` annotation is not necessary. 50 | 51 | ```java 52 | @Component 53 | public class NewComponent { 54 | 55 | @Autowired 56 | public NewComponent() { 57 | // put object into a consistent state ready for interacting with. 58 | } 59 | } 60 | ``` 61 | 62 | If you want to utilise an existing service, you just need the configuration similar to below: 63 | 64 | ```java 65 | @SpringBootConfiguration 66 | public class ApplicationConfiguration { 67 | 68 | @Bean 69 | public ObjectMapper objectMapper() { 70 | ObjectMapper objectMapper = new ObjectMapper(); 71 | objectMapper.registerModule(new JavaTimeModule()); 72 | return objectMapper; 73 | } 74 | 75 | @Bean 76 | public Signature signature(@Value("${exchange.secret}") String secret) { 77 | return new Signature(secret); 78 | } 79 | 80 | @Bean 81 | public CoinbaseExchange coinbasePro(@Value("${exchange.key}") String publicKey, 82 | @Value("${exchange.passphrase}") String passphrase, 83 | @Value("${exchange.baseUrl}") String baseUrl, 84 | Signature signature, 85 | ObjectMapper objectMapper) { 86 | return new CoinbaseExchangeImpl(publicKey, passphrase, baseUrl, signature, objectMapper); 87 | } 88 | 89 | /** 90 | * now you can create services by wiring in the CoinbaseExchange object to handle incoming/outgoing requests. 91 | **/ 92 | 93 | @Bean 94 | public AccountService accountService(CoinbaseExchange exchange) { 95 | return new AccountService(exchange); 96 | } 97 | } 98 | ``` 99 | 100 | ```java 101 | @Component 102 | public class MyApplication { 103 | 104 | private AccountService accountService; 105 | 106 | @Autowired 107 | public MyApplication(AccountService accountService) { 108 | this.accountService = accountService; 109 | } 110 | 111 | public void printMyAccounts() { 112 | List accounts = accountService.getAccounts(); 113 | for(Account account: accounts) { 114 | System.out.println(accounts.getBalance()); 115 | } 116 | } 117 | } 118 | ``` 119 | 120 | If you can do TDD, great. If not, add it after you've coded a solution. 121 | 122 | Tests all follow a "given, when, then" style... *given* some preconditions `X`, *when* I exercise a given method `Y` (typically a method call on its own line), *then* the results are expected to be `Z` (if not the test fails). -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ishmael Rufus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /api/build.gradle: -------------------------------------------------------------------------------- 1 | test { 2 | useJUnitPlatform() 3 | } 4 | 5 | dependencies { 6 | compile project(':model') 7 | compile project(':security') 8 | implementation 'com.fasterxml.jackson.core:jackson-core' 9 | implementation 'com.fasterxml.jackson.core:jackson-databind' 10 | implementation 'com.fasterxml.jackson.core:jackson-annotations' 11 | implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8' 12 | implementation 'org.springframework.boot:spring-boot-starter-web' 13 | implementation 'org.springframework:spring-context' 14 | implementation 'org.slf4j:slf4j-api' 15 | implementation 'ch.qos.logback:logback-classic' 16 | implementation 'ch.qos.logback:logback-core' 17 | 18 | testImplementation 'junit:junit' 19 | testImplementation 'org.junit.jupiter:junit-jupiter-api' 20 | testImplementation 'org.junit.jupiter:junit-jupiter-params' 21 | testImplementation 'org.junit.jupiter:junit-jupiter-engine' 22 | testImplementation 'org.junit.platform:junit-platform-suite-api' 23 | testImplementation 'org.springframework.boot:spring-boot-test' 24 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 25 | } -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/accounts/Account.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.accounts; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * Created by irufus on 2/18/15. 7 | */ 8 | public class Account { 9 | private String id; 10 | private String currency; 11 | private BigDecimal balance; 12 | private BigDecimal available; 13 | private BigDecimal hold; 14 | private String profile_id; 15 | 16 | public Account() { 17 | this.balance = BigDecimal.ZERO; 18 | this.available = BigDecimal.ZERO; 19 | this.hold = BigDecimal.ZERO; 20 | } 21 | 22 | public Account(String id, 23 | String currency, 24 | BigDecimal balance, 25 | BigDecimal available, 26 | BigDecimal hold, 27 | String profile_id) { 28 | this.id = id; 29 | this.currency = currency; 30 | this.balance = balance; 31 | this.available = available; 32 | this.hold = hold; 33 | this.profile_id = profile_id; 34 | } 35 | 36 | public String getId() { 37 | return id; 38 | } 39 | 40 | public void setId(String id) { 41 | this.id = id; 42 | } 43 | 44 | public BigDecimal getBalance() { 45 | return balance; 46 | } 47 | 48 | public void setBalance(BigDecimal balance) { 49 | this.balance = balance; 50 | } 51 | 52 | public BigDecimal getHold() { 53 | return hold; 54 | } 55 | 56 | public void setHold(BigDecimal hold) { 57 | this.hold = hold; 58 | } 59 | 60 | public BigDecimal getAvailable() { 61 | return available; 62 | } 63 | 64 | public void setAvailable(BigDecimal available) { 65 | this.available = available; 66 | } 67 | 68 | public String getCurrency() { 69 | return currency; 70 | } 71 | 72 | public void setCurrency(String currency) { 73 | this.currency = currency; 74 | } 75 | 76 | public String getProfile_id() { 77 | return profile_id; 78 | } 79 | 80 | public void setProfile_id(String profile_id) { 81 | this.profile_id = profile_id; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/accounts/AccountHistory.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.accounts; 2 | 3 | import com.coinbase.exchange.model.Detail; 4 | 5 | import java.math.BigDecimal; 6 | 7 | /** 8 | * Created by irufus on 2/18/15. 9 | */ 10 | public class AccountHistory { 11 | private Integer id; 12 | private String created_at; 13 | private BigDecimal amount; 14 | private BigDecimal balance; 15 | private String type; 16 | private Detail detail; 17 | 18 | public AccountHistory() {} 19 | 20 | public Integer getId() { 21 | return id; 22 | } 23 | 24 | public void setId(Integer id) { 25 | this.id = id; 26 | } 27 | 28 | public String getCreated_at() { 29 | return created_at; 30 | } 31 | 32 | public void setCreated_at(String created_at) { 33 | this.created_at = created_at; 34 | } 35 | 36 | public BigDecimal getAmount() { 37 | return amount; 38 | } 39 | 40 | public void setAmount(BigDecimal amount) { 41 | this.amount = amount; 42 | } 43 | 44 | public BigDecimal getBalance() { 45 | return balance; 46 | } 47 | 48 | public void setBalance(BigDecimal balance) { 49 | this.balance = balance; 50 | } 51 | 52 | public String getType() { 53 | return type; 54 | } 55 | 56 | public void setType(String type) { 57 | this.type = type; 58 | } 59 | 60 | public Detail getDetail() { 61 | return detail; 62 | } 63 | 64 | public void setDetail(Detail detail) { 65 | this.detail = detail; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/accounts/AccountService.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.accounts; 2 | 3 | import com.coinbase.exchange.model.Hold; 4 | import com.coinbase.exchange.api.exchange.CoinbaseExchange; 5 | import org.springframework.core.ParameterizedTypeReference; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by robevansuk on 25/01/2017. 11 | */ 12 | public class AccountService { 13 | 14 | final CoinbaseExchange exchange; 15 | 16 | public AccountService(final CoinbaseExchange exchange) { 17 | this.exchange = exchange; 18 | } 19 | 20 | public static final String ACCOUNTS_ENDPOINT = "/accounts"; 21 | 22 | public List getAccounts(){ 23 | return exchange.getAsList(ACCOUNTS_ENDPOINT, new ParameterizedTypeReference(){}); 24 | } 25 | 26 | public Account getAccount(String id) { 27 | return exchange.get(ACCOUNTS_ENDPOINT + "/" + id, new ParameterizedTypeReference() {}); 28 | } 29 | 30 | public List getAccountHistory(String accountId) { 31 | String accountHistoryEndpoint = ACCOUNTS_ENDPOINT + "/" + accountId + "/ledger"; 32 | return exchange.getAsList(accountHistoryEndpoint, new ParameterizedTypeReference(){}); 33 | } 34 | 35 | public List getPagedAccountHistory(String accountId, 36 | String beforeOrAfter, 37 | Integer pageNumber, 38 | Integer limit) { 39 | 40 | String accountHistoryEndpoint = ACCOUNTS_ENDPOINT + "/" + accountId + "/ledger"; 41 | return exchange.pagedGetAsList(accountHistoryEndpoint, 42 | new ParameterizedTypeReference(){}, 43 | beforeOrAfter, 44 | pageNumber, 45 | limit); 46 | } 47 | 48 | public List getHolds(String accountId) { 49 | String holdsEndpoint = ACCOUNTS_ENDPOINT + "/" + accountId + "/holds"; 50 | return exchange.getAsList(holdsEndpoint, new ParameterizedTypeReference(){}); 51 | } 52 | 53 | public List getPagedHolds(String accountId, 54 | String beforeOrAfter, 55 | Integer pageNumber, 56 | Integer limit) { 57 | String holdsEndpoint = ACCOUNTS_ENDPOINT + "/" + accountId + "/holds"; 58 | return exchange.pagedGetAsList(holdsEndpoint, 59 | new ParameterizedTypeReference(){}, 60 | beforeOrAfter, 61 | pageNumber, 62 | limit); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/deposits/DepositService.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.deposits; 2 | 3 | import com.coinbase.exchange.model.CoinbasePaymentRequest; 4 | import com.coinbase.exchange.model.PaymentResponse; 5 | import com.coinbase.exchange.api.exchange.CoinbaseExchange; 6 | import org.springframework.core.ParameterizedTypeReference; 7 | 8 | import java.math.BigDecimal; 9 | 10 | /** 11 | * Created by robevansuk on 16/02/2017. 12 | */ 13 | public class DepositService { 14 | 15 | private static final String DEPOSIT_ENDPOINT = "/deposits"; 16 | private static final String PAYMENTS = "/payment-method"; 17 | private static final String COINBASE_PAYMENT = "/coinbase-account"; 18 | 19 | final CoinbaseExchange exchange; 20 | 21 | public DepositService(final CoinbaseExchange exchange) { 22 | this.exchange = exchange; 23 | } 24 | 25 | /** 26 | * @param amount 27 | * @param currency 28 | * @param paymentMethodId 29 | * @return PaymentResponse 30 | */ 31 | public PaymentResponse depositViaPaymentMethod(BigDecimal amount, String currency, final String paymentMethodId) { 32 | CoinbasePaymentRequest coinbasePaymentRequest = new CoinbasePaymentRequest(amount, currency, paymentMethodId); 33 | return exchange.post(DEPOSIT_ENDPOINT + PAYMENTS, 34 | new ParameterizedTypeReference(){}, 35 | coinbasePaymentRequest); 36 | } 37 | 38 | /** 39 | * @param amount 40 | * @param currency 41 | * @param coinbaseAccountId 42 | * @return PaymentResponse 43 | */ 44 | public PaymentResponse depositViaCoinbase(BigDecimal amount, String currency, String coinbaseAccountId) { 45 | CoinbasePaymentRequest coinbasePaymentRequest = new CoinbasePaymentRequest(amount, currency, coinbaseAccountId); 46 | return exchange.post(DEPOSIT_ENDPOINT + COINBASE_PAYMENT, 47 | new ParameterizedTypeReference(){}, 48 | coinbasePaymentRequest); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/exchange/CoinbaseExchange.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.exchange; 2 | 3 | import org.springframework.core.ParameterizedTypeReference; 4 | import org.springframework.http.HttpEntity; 5 | 6 | import java.util.List; 7 | 8 | public interface CoinbaseExchange { 9 | 10 | String getBaseUrl(); 11 | HttpEntity securityHeaders(String endpoint, String method, String body); 12 | T get(String endpoint, ParameterizedTypeReference type); 13 | T pagedGet(String endpoint, ParameterizedTypeReference responseType, String beforeOrAfter, Integer pageNumber, Integer limit); 14 | List getAsList(String endpoint, ParameterizedTypeReference type); 15 | List pagedGetAsList(String endpoint, ParameterizedTypeReference responseType, String beforeOrAfter, Integer pageNumber, Integer limit); 16 | T post(String endpoint, ParameterizedTypeReference type, R jsonObject); 17 | T delete(String endpoint, ParameterizedTypeReference type); 18 | } 19 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/marketdata/MarketData.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.marketdata; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by robevansuk on 09/02/2017. 7 | */ 8 | public class MarketData { 9 | 10 | private Long sequence; 11 | private List bids; // price, size, orders 12 | private List asks; // price, size, orders 13 | 14 | public MarketData() { } 15 | 16 | public MarketData(Long sequence, List bids, List asks) { 17 | this.sequence = sequence; 18 | this.bids = bids; 19 | this.asks = asks; 20 | } 21 | 22 | public Long getSequence() { 23 | return sequence; 24 | } 25 | 26 | public void setSequence(Long sequence) { 27 | this.sequence = sequence; 28 | } 29 | 30 | public List getBids() { 31 | return bids; 32 | } 33 | 34 | public void setBids(List bids) { 35 | this.bids = bids; 36 | } 37 | 38 | public List getAsks() { 39 | return asks; 40 | } 41 | 42 | public void setAsks(List asks) { 43 | this.asks = asks; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/marketdata/MarketDataService.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.marketdata; 2 | 3 | import com.coinbase.exchange.api.exchange.CoinbaseExchange; 4 | import org.springframework.core.ParameterizedTypeReference; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by robevansuk on 07/02/2017. 10 | */ 11 | public class MarketDataService { 12 | 13 | final CoinbaseExchange exchange; 14 | 15 | public MarketDataService(final CoinbaseExchange exchange) { 16 | this.exchange = exchange; 17 | } 18 | 19 | public static final String PRODUCT_ENDPOINT = "/products"; 20 | 21 | public MarketData getMarketDataOrderBook(String productId, int level) { 22 | String marketDataEndpoint = PRODUCT_ENDPOINT + "/" + productId + "/book"; 23 | if(level != 1) 24 | marketDataEndpoint += "?level=" + level; 25 | return exchange.get(marketDataEndpoint, new ParameterizedTypeReference(){}); 26 | } 27 | 28 | public List getTrades(String productId) { 29 | String tradesEndpoint = PRODUCT_ENDPOINT + "/" + productId + "/trades"; 30 | return exchange.getAsList(tradesEndpoint, new ParameterizedTypeReference(){}); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/marketdata/Message.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.marketdata; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * Created by irufus on 2/28/15. 7 | */ 8 | public class Message { 9 | private String type; 10 | private Long sequence; 11 | private String order_id; 12 | private BigDecimal size; 13 | private BigDecimal price; 14 | private String side; 15 | private BigDecimal remaining_size; 16 | private String reason; 17 | private String maker_order_id; 18 | private String taker_order_id; 19 | private String time; 20 | 21 | public BigDecimal getRemaining_size() { 22 | return remaining_size; 23 | } 24 | 25 | public void setRemaining_size(BigDecimal remaining_size) { 26 | this.remaining_size = remaining_size; 27 | } 28 | 29 | public String getSide() { 30 | return side; 31 | } 32 | 33 | public void setSide(String side) { 34 | this.side = side; 35 | } 36 | 37 | public BigDecimal getPrice() { 38 | return price; 39 | } 40 | 41 | public void setPrice(BigDecimal price) { 42 | this.price = price; 43 | } 44 | 45 | public BigDecimal getSize() { 46 | return size; 47 | } 48 | 49 | public void setSize(BigDecimal size) { 50 | this.size = size; 51 | } 52 | 53 | public String getOrder_id() { 54 | return order_id; 55 | } 56 | 57 | public void setOrder_id(String order_id) { 58 | this.order_id = order_id; 59 | } 60 | 61 | public Long getSequence() { 62 | return sequence; 63 | } 64 | 65 | public void setSequence(Long sequence) { 66 | this.sequence = sequence; 67 | } 68 | 69 | public String getType() { 70 | return type; 71 | } 72 | 73 | public void setType(String type) { 74 | this.type = type; 75 | } 76 | 77 | public String getReason() { 78 | return reason; 79 | } 80 | 81 | public void setReason(String reason) { 82 | this.reason = reason; 83 | } 84 | 85 | public String getMaker_order_id() { 86 | return maker_order_id; 87 | } 88 | 89 | public void setMaker_order_id(String maker_order_id) { 90 | this.maker_order_id = maker_order_id; 91 | } 92 | 93 | public String getTaker_order_id() { 94 | return taker_order_id; 95 | } 96 | 97 | public void setTaker_order_id(String taker_order_id) { 98 | this.taker_order_id = taker_order_id; 99 | } 100 | 101 | public String getTime() { 102 | return time; 103 | } 104 | 105 | public void setTime(String time) { 106 | this.time = time; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/marketdata/MessageEX.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.marketdata; 2 | 3 | /** 4 | * Created by irufus on 3/2/15. 5 | */ 6 | public class MessageEX { 7 | public static class MessageType{ 8 | public final static String RECEIEVED = "received"; 9 | public final static String OPEN = "open"; 10 | public final static String DONE = "done"; 11 | public final static String MATCH = "match"; 12 | public final static String CHANGE = "change"; 13 | public final static String ERROR = "error"; 14 | } 15 | public static class MessageSide{ 16 | public final static String BUY = "buy"; 17 | public final static String SELL = "sell"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/marketdata/OrderItem.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.marketdata; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import org.springframework.util.CollectionUtils; 5 | 6 | import java.math.BigDecimal; 7 | import java.util.List; 8 | 9 | /** 10 | * Created by robevansuk on 20/03/2017. 11 | */ 12 | public class OrderItem implements Comparable { 13 | 14 | private final BigDecimal price; 15 | private final BigDecimal size; 16 | private final String orderId; 17 | private final BigDecimal num; 18 | 19 | @JsonCreator 20 | public OrderItem(List limitOrders) { 21 | if (CollectionUtils.isEmpty(limitOrders) || limitOrders.size() < 3) { 22 | throw new IllegalArgumentException("LimitOrders was empty - check connection to the exchange"); 23 | } 24 | price = new BigDecimal(limitOrders.get(0)); 25 | size = new BigDecimal(limitOrders.get(1)); 26 | if (isString(limitOrders.get(2))) { 27 | orderId = limitOrders.get(2); 28 | num = new BigDecimal(1); 29 | } else { 30 | orderId = null; 31 | num = new BigDecimal(1); 32 | } 33 | } 34 | 35 | public BigDecimal getPrice() { 36 | return price; 37 | } 38 | 39 | public BigDecimal getSize() { 40 | return size; 41 | } 42 | 43 | public String getOrderId() { 44 | return orderId; 45 | } 46 | 47 | public BigDecimal getNum() { 48 | return num; 49 | } 50 | 51 | @Override 52 | public int compareTo(Object o) { 53 | return this.getPrice().compareTo(((OrderItem)o).getPrice()) * -1; 54 | } 55 | 56 | public boolean isString(String value) { 57 | boolean isDecimalSeparatorFound = false; 58 | 59 | for (char c : value.substring( 1 ).toCharArray()) { 60 | if (!Character.isDigit( c ) ) { 61 | if (c == '.' && !isDecimalSeparatorFound) { 62 | isDecimalSeparatorFound = true; 63 | continue; 64 | } 65 | return false; 66 | } 67 | } 68 | return true; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/marketdata/Trade.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.marketdata; 2 | 3 | import java.math.BigDecimal; 4 | import java.time.Instant; 5 | 6 | /** 7 | * Created by robevansuk on 12/03/2017. 8 | */ 9 | public class Trade { 10 | Instant time; 11 | Long trade_id; 12 | BigDecimal price; 13 | BigDecimal size; 14 | String side; 15 | 16 | public Trade() {} 17 | 18 | public Trade(Instant time, Long trade_id, BigDecimal price, BigDecimal size, String side) { 19 | this.time = time; 20 | this.trade_id = trade_id; 21 | this.price = price; 22 | this.size = size; 23 | this.side = side; 24 | } 25 | 26 | public Instant getTime() { 27 | return time; 28 | } 29 | 30 | public void setTime(Instant time) { 31 | this.time = time; 32 | } 33 | 34 | public Long getTrade_id() { 35 | return trade_id; 36 | } 37 | 38 | public void setTrade_id(Long trade_id) { 39 | this.trade_id = trade_id; 40 | } 41 | 42 | public BigDecimal getPrice() { 43 | return price; 44 | } 45 | 46 | public void setPrice(BigDecimal price) { 47 | this.price = price; 48 | } 49 | 50 | public BigDecimal getSize() { 51 | return size; 52 | } 53 | 54 | public void setSize(BigDecimal size) { 55 | this.size = size; 56 | } 57 | 58 | public String getSide() { 59 | return side; 60 | } 61 | 62 | public void setSide(String side) { 63 | this.side = side; 64 | } 65 | 66 | public String toString(){ 67 | return side.toUpperCase() + " " + size +"@" + price; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/orders/Order.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.orders; 2 | 3 | /** 4 | * Created by robevansuk on 03/02/2017. 5 | */ 6 | public class Order { 7 | String id; 8 | String size; 9 | String price; 10 | String product_id; 11 | String side; 12 | String stp; 13 | String type; 14 | String time_in_force; 15 | String post_only; 16 | String created_at; 17 | String fill_fees; 18 | String filled_size; 19 | String executed_value; 20 | String status; 21 | 22 | public Order() {} 23 | 24 | public Order(String id, String price, String size, String product_id, String side, String stp, String type, 25 | String time_in_force, String post_only, String created_at, String fill_fees, String filled_size, 26 | String executed_value, String status, Boolean settled) { 27 | this.id = id; 28 | this.price = price; 29 | this.size = size; 30 | this.product_id = product_id; 31 | this.side = side; 32 | this.stp = stp; 33 | this.type = type; 34 | this.time_in_force = time_in_force; 35 | this.post_only = post_only; 36 | this.created_at = created_at; 37 | this.fill_fees = fill_fees; 38 | this.filled_size = filled_size; 39 | this.executed_value = executed_value; 40 | this.status = status; 41 | this.settled = settled; 42 | } 43 | 44 | Boolean settled; 45 | 46 | public Order(OrderBuilder builder) { 47 | this.id = builder.getId(); 48 | this.size = builder.getSize(); 49 | this.price = builder.getPrice(); 50 | this.product_id = builder.getProduct_id(); 51 | this.status = builder.getStatus(); 52 | this.filled_size = builder.getFilled_size(); 53 | this.fill_fees = builder.getFill_fees(); 54 | this.settled = builder.getSettled(); 55 | this.side = builder.getSide(); 56 | this.created_at = builder.getCreated_at(); 57 | } 58 | 59 | public String getId() { 60 | return id; 61 | } 62 | 63 | public void setId(String id) { 64 | this.id = id; 65 | } 66 | 67 | public String getPrice() { 68 | return price; 69 | } 70 | 71 | public void setPrice(String price) { 72 | this.price = price; 73 | } 74 | 75 | public String getSize() { 76 | return size; 77 | } 78 | 79 | public void setSize(String size) { 80 | this.size = size; 81 | } 82 | 83 | public String getProduct_id() { 84 | return product_id; 85 | } 86 | 87 | public void setProduct_id(String product_id) { 88 | this.product_id = product_id; 89 | } 90 | 91 | public String getSide() { 92 | return side; 93 | } 94 | 95 | public void setSide(String side) { 96 | this.side = side; 97 | } 98 | 99 | public String getStp() { 100 | return stp; 101 | } 102 | 103 | public void setStp(String stp) { 104 | this.stp = stp; 105 | } 106 | 107 | public String getType() { 108 | return type; 109 | } 110 | 111 | public void setType(String type) { 112 | this.type = type; 113 | } 114 | 115 | public String getTime_in_force() { 116 | return time_in_force; 117 | } 118 | 119 | public void setTime_in_force(String time_in_force) { 120 | this.time_in_force = time_in_force; 121 | } 122 | 123 | public String getPost_only() { 124 | return post_only; 125 | } 126 | 127 | public void setPost_only(String post_only) { 128 | this.post_only = post_only; 129 | } 130 | 131 | public String getCreated_at() { 132 | return created_at; 133 | } 134 | 135 | public void setCreated_at(String created_at) { 136 | this.created_at = created_at; 137 | } 138 | 139 | public String getFill_fees() { 140 | return fill_fees; 141 | } 142 | 143 | public void setFill_fees(String fill_fees) { 144 | this.fill_fees = fill_fees; 145 | } 146 | 147 | public String getFilled_size() { 148 | return filled_size; 149 | } 150 | 151 | public void setFilled_size(String filled_size) { 152 | this.filled_size = filled_size; 153 | } 154 | 155 | public String getExecuted_value() { 156 | return executed_value; 157 | } 158 | 159 | public void setExecuted_value(String executed_value) { 160 | this.executed_value = executed_value; 161 | } 162 | 163 | public String getStatus() { 164 | return status; 165 | } 166 | 167 | public void setStatus(String status) { 168 | this.status = status; 169 | } 170 | 171 | public Boolean getSettled() { 172 | return settled; 173 | } 174 | 175 | public void setSettled(Boolean settled) { 176 | this.settled = settled; 177 | } 178 | 179 | public String toString() { 180 | String orderString = getSide(); 181 | orderString += ": " + getProduct_id(); 182 | orderString += ": " + getPrice(); 183 | orderString += ": " + getSize(); 184 | return orderString; 185 | } 186 | 187 | } 188 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/orders/OrderBuilder.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.orders; 2 | 3 | /** 4 | * Created by irufus on 2/18/15. 5 | */ 6 | public class OrderBuilder { 7 | 8 | private String id; 9 | private String size; 10 | private String price; 11 | private String product_id; 12 | private String status; 13 | private String filled_size; 14 | private String fill_fees; 15 | private String side; 16 | private String created_at; 17 | private Boolean settled; 18 | 19 | public OrderBuilder setId(String id) { 20 | this.id = id; 21 | return this; 22 | } 23 | 24 | public OrderBuilder setSize(String size) { 25 | this.size = size; 26 | return this; 27 | } 28 | 29 | public OrderBuilder setPrice(String price) { 30 | this.price = price; 31 | return this; 32 | } 33 | 34 | public OrderBuilder setProduct_id(String product_id) { 35 | this.product_id = product_id; 36 | return this; 37 | } 38 | 39 | public OrderBuilder setStatus(String status) { 40 | this.status = status; 41 | return this; 42 | } 43 | 44 | public OrderBuilder setFilled_size(String filled_size) { 45 | this.filled_size = filled_size; 46 | return this; 47 | } 48 | 49 | public OrderBuilder setFill_fees(String fill_fees) { 50 | this.fill_fees = fill_fees; 51 | return this; 52 | } 53 | 54 | public OrderBuilder setSettled(Boolean settled) { 55 | this.settled = settled; 56 | return this; 57 | } 58 | 59 | public OrderBuilder setSide(String side) { 60 | this.side = side; 61 | return this; 62 | } 63 | 64 | public OrderBuilder setCreated_at(String created_at) { 65 | this.created_at = created_at; 66 | return this; 67 | } 68 | 69 | public String getId() { 70 | return id; 71 | } 72 | 73 | public String getSize() { 74 | return size; 75 | } 76 | 77 | public String getPrice() { 78 | return price; 79 | } 80 | 81 | public String getProduct_id() { 82 | return product_id; 83 | } 84 | 85 | public String getStatus() { 86 | return status; 87 | } 88 | 89 | public String getFilled_size() { 90 | return filled_size; 91 | } 92 | 93 | public String getFill_fees() { 94 | return fill_fees; 95 | } 96 | 97 | public Boolean getSettled() { 98 | return settled; 99 | } 100 | 101 | public String getSide() { 102 | return side; 103 | } 104 | 105 | public String getCreated_at() { 106 | return created_at; 107 | } 108 | 109 | public Order build() { 110 | return new Order(this); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/orders/OrderService.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.orders; 2 | 3 | import com.coinbase.exchange.model.Fill; 4 | import com.coinbase.exchange.model.Hold; 5 | import com.coinbase.exchange.model.NewOrderSingle; 6 | import com.coinbase.exchange.api.exchange.CoinbaseExchange; 7 | import org.springframework.core.ParameterizedTypeReference; 8 | 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | /** 13 | * Created by robevansuk on 03/02/2017. 14 | */ 15 | public class OrderService { 16 | public static final String ORDERS_ENDPOINT = "/orders"; 17 | public static final String FILLS_ENDPOINT = "/fills"; 18 | 19 | final CoinbaseExchange exchange; 20 | 21 | public OrderService(final CoinbaseExchange exchange) { 22 | this.exchange = exchange; 23 | } 24 | 25 | public List getHolds(String accountId) { 26 | return exchange.getAsList(ORDERS_ENDPOINT + "/" + accountId + "/holds", new ParameterizedTypeReference(){}); 27 | } 28 | 29 | public List getOpenOrders(String accountId) { 30 | return exchange.getAsList(ORDERS_ENDPOINT + "/" + accountId + "/orders", new ParameterizedTypeReference(){}); 31 | } 32 | 33 | public Order getOrder(String orderId) { 34 | return exchange.get(ORDERS_ENDPOINT + "/" + orderId,new ParameterizedTypeReference(){}); 35 | } 36 | 37 | public Order createOrder(NewOrderSingle order) { 38 | return exchange.post(ORDERS_ENDPOINT, new ParameterizedTypeReference(){}, order); 39 | } 40 | 41 | public String cancelOrder(String orderId) { 42 | String deleteEndpoint = ORDERS_ENDPOINT + "/" + orderId; 43 | return exchange.delete(deleteEndpoint, new ParameterizedTypeReference(){}); 44 | } 45 | 46 | public List getOpenOrders() { 47 | return exchange.getAsList(ORDERS_ENDPOINT, new ParameterizedTypeReference(){}); 48 | } 49 | 50 | public List cancelAllOpenOrders() { 51 | return Arrays.asList(exchange.delete(ORDERS_ENDPOINT, new ParameterizedTypeReference(){})); 52 | } 53 | 54 | public List getFillsByProductId(String product_id, int resultLimit) { 55 | return exchange.getAsList(FILLS_ENDPOINT + "?product_id=" + product_id + "&limit=" + resultLimit, new ParameterizedTypeReference(){}); 56 | } 57 | 58 | public List getFillByOrderId(String order_id, int resultLimit) { 59 | return exchange.getAsList(FILLS_ENDPOINT + "?order_id=" + order_id + "&limit=" + resultLimit, new ParameterizedTypeReference(){}); 60 | } 61 | } 62 | 63 | 64 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/payments/AccountLimit.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.payments; 2 | 3 | /** 4 | * Created by robevansuk on 16/02/2017. 5 | */ 6 | public class AccountLimit { 7 | 8 | Integer period_in_days; 9 | Amount total; 10 | Amount remaining; 11 | 12 | public AccountLimit() {} 13 | 14 | public AccountLimit(Integer period_in_days, Amount total, Amount remaining) { 15 | this.period_in_days = period_in_days; 16 | this.total = total; 17 | this.remaining = remaining; 18 | } 19 | 20 | public Integer getPeriod_in_days() { 21 | return period_in_days; 22 | } 23 | 24 | public void setPeriod_in_days(Integer period_in_days) { 25 | this.period_in_days = period_in_days; 26 | } 27 | 28 | public Amount getTotal() { 29 | return total; 30 | } 31 | 32 | public void setTotal(Amount total) { 33 | this.total = total; 34 | } 35 | 36 | public Amount getRemaining() { 37 | return remaining; 38 | } 39 | 40 | public void setRemaining(Amount remaining) { 41 | this.remaining = remaining; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/payments/Amount.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.payments; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * Created by robevansuk on 16/02/2017. 7 | */ 8 | public class Amount { 9 | 10 | BigDecimal amount; 11 | String currency; 12 | 13 | public Amount() {} 14 | 15 | public Amount(BigDecimal amount, String currency) { 16 | this.amount = amount; 17 | this.currency = currency; 18 | } 19 | 20 | public BigDecimal getAmount() { 21 | return amount; 22 | } 23 | 24 | public void setAmount(BigDecimal amount) { 25 | this.amount = amount; 26 | } 27 | 28 | public String getCurrency() { 29 | return currency; 30 | } 31 | 32 | public void setCurrency(String currency) { 33 | this.currency = currency; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/payments/BankCountry.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.payments; 2 | 3 | /** 4 | * Created by robevansuk on 16/02/2017. 5 | */ 6 | public class BankCountry { 7 | 8 | String code; 9 | String name; 10 | 11 | public BankCountry() {} 12 | 13 | public BankCountry(String code, String name) { 14 | this.code = code; 15 | this.name = name; 16 | } 17 | 18 | public String getCode() { 19 | return code; 20 | } 21 | 22 | public void setCode(String code) { 23 | this.code = code; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/payments/CoinbaseAccount.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.payments; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * Created by robevansuk on 16/02/2017. 7 | */ 8 | public class CoinbaseAccount { 9 | 10 | private String id; //UUID 11 | private String name; 12 | private BigDecimal balance; 13 | private String currency; 14 | private String type; 15 | private Boolean primary; 16 | private Boolean active; 17 | private DepositInformation wire_deposit_information; 18 | private SepaDepositInformation sepa_deposit_information; 19 | 20 | public CoinbaseAccount() {} 21 | 22 | public CoinbaseAccount(String id, 23 | String name, 24 | BigDecimal balance, 25 | String currency, 26 | String type, 27 | Boolean primary, 28 | Boolean active, 29 | DepositInformation wire_deposit_information, 30 | SepaDepositInformation sepa_deposit_information) { 31 | this.id = id; 32 | this.name = name; 33 | this.balance = balance; 34 | this.currency = currency; 35 | this.type = type; 36 | this.primary = primary; 37 | this.active = active; 38 | this.wire_deposit_information = wire_deposit_information; 39 | this.sepa_deposit_information = sepa_deposit_information; 40 | } 41 | 42 | public String getId() { 43 | return id; 44 | } 45 | 46 | public void setId(String id) { 47 | this.id = id; 48 | } 49 | 50 | public String getName() { 51 | return name; 52 | } 53 | 54 | public void setName(String name) { 55 | this.name = name; 56 | } 57 | 58 | public BigDecimal getBalance() { 59 | return balance; 60 | } 61 | 62 | public void setBalance(BigDecimal balance) { 63 | this.balance = balance; 64 | } 65 | 66 | public String getCurrency() { 67 | return currency; 68 | } 69 | 70 | public void setCurrency(String currency) { 71 | this.currency = currency; 72 | } 73 | 74 | public String getType() { 75 | return type; 76 | } 77 | 78 | public void setType(String type) { 79 | this.type = type; 80 | } 81 | 82 | public Boolean getPrimary() { 83 | return primary; 84 | } 85 | 86 | public void setPrimary(Boolean primary) { 87 | this.primary = primary; 88 | } 89 | 90 | public Boolean getActive() { 91 | return active; 92 | } 93 | 94 | public void setActive(Boolean active) { 95 | this.active = active; 96 | } 97 | 98 | public DepositInformation getWire_deposit_information() { 99 | return wire_deposit_information; 100 | } 101 | 102 | public void setWire_deposit_information(DepositInformation wire_deposit_information) { 103 | this.wire_deposit_information = wire_deposit_information; 104 | } 105 | 106 | public SepaDepositInformation getSepa_deposit_information() { 107 | return sepa_deposit_information; 108 | } 109 | 110 | public void setSepa_deposit_information(SepaDepositInformation sepa_deposit_information) { 111 | this.sepa_deposit_information = sepa_deposit_information; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/payments/DepositInformation.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.payments; 2 | 3 | /** 4 | * Created by robevansuk on 16/02/2017. 5 | */ 6 | public class DepositInformation { 7 | 8 | String account_number; 9 | String routing_number; 10 | String bank_name; 11 | String bank_address; 12 | BankCountry bank_country; 13 | String account_name; 14 | String account_address; 15 | String reference; 16 | 17 | public DepositInformation() {} 18 | 19 | public DepositInformation(String account_number, 20 | String routing_number, 21 | String bank_name, 22 | String bank_address, 23 | BankCountry bank_country, 24 | String account_name, 25 | String account_address, 26 | String reference) { 27 | this.account_number = account_number; 28 | this.routing_number = routing_number; 29 | this.bank_name = bank_name; 30 | this.bank_address = bank_address; 31 | this.bank_country = bank_country; 32 | this.account_name = account_name; 33 | this.account_address = account_address; 34 | this.reference = reference; 35 | } 36 | 37 | public String getAccount_number() { 38 | return account_number; 39 | } 40 | 41 | public void setAccount_number(String account_number) { 42 | this.account_number = account_number; 43 | } 44 | 45 | public String getRouting_number() { 46 | return routing_number; 47 | } 48 | 49 | public void setRouting_number(String routing_number) { 50 | this.routing_number = routing_number; 51 | } 52 | 53 | public String getBank_name() { 54 | return bank_name; 55 | } 56 | 57 | public void setBank_name(String bank_name) { 58 | this.bank_name = bank_name; 59 | } 60 | 61 | public String getBank_address() { 62 | return bank_address; 63 | } 64 | 65 | public void setBank_address(String bank_address) { 66 | this.bank_address = bank_address; 67 | } 68 | 69 | public BankCountry getBank_country() { 70 | return bank_country; 71 | } 72 | 73 | public void setBank_country(BankCountry bank_country) { 74 | this.bank_country = bank_country; 75 | } 76 | 77 | public String getAccount_name() { 78 | return account_name; 79 | } 80 | 81 | public void setAccount_name(String account_name) { 82 | this.account_name = account_name; 83 | } 84 | 85 | public String getAccount_address() { 86 | return account_address; 87 | } 88 | 89 | public void setAccount_address(String account_address) { 90 | this.account_address = account_address; 91 | } 92 | 93 | public String getReference() { 94 | return reference; 95 | } 96 | 97 | public void setReference(String reference) { 98 | this.reference = reference; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/payments/Limit.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.payments; 2 | 3 | /** 4 | * Created by robevansuk on 16/02/2017. 5 | */ 6 | public class Limit { 7 | 8 | AccountLimit[] buy; 9 | AccountLimit[] instant_buy; 10 | AccountLimit[] sell; 11 | AccountLimit[] deposit; 12 | 13 | public Limit() {} 14 | 15 | public Limit(AccountLimit[] buy, AccountLimit[] instant_buy, AccountLimit[] sell, AccountLimit[] deposit) { 16 | this.buy = buy; 17 | this.instant_buy = instant_buy; 18 | this.sell = sell; 19 | this.deposit = deposit; 20 | } 21 | 22 | public AccountLimit[] getBuy() { 23 | return buy; 24 | } 25 | 26 | public void setBuy(AccountLimit[] buy) { 27 | this.buy = buy; 28 | } 29 | 30 | public AccountLimit[] getInstant_buy() { 31 | return instant_buy; 32 | } 33 | 34 | public void setInstant_buy(AccountLimit[] instant_buy) { 35 | this.instant_buy = instant_buy; 36 | } 37 | 38 | public AccountLimit[] getSell() { 39 | return sell; 40 | } 41 | 42 | public void setSell(AccountLimit[] sell) { 43 | this.sell = sell; 44 | } 45 | 46 | public AccountLimit[] getDeposit() { 47 | return deposit; 48 | } 49 | 50 | public void setDeposit(AccountLimit[] deposit) { 51 | this.deposit = deposit; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/payments/PaymentService.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.payments; 2 | 3 | import com.coinbase.exchange.api.exchange.CoinbaseExchange; 4 | import org.springframework.core.ParameterizedTypeReference; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by robevansuk on 16/02/2017. 10 | */ 11 | public class PaymentService { 12 | 13 | private static final String PAYMENT_METHODS_ENDPOINT = "/payment-methods"; 14 | private static final String COINBASE_ACCOUNTS_ENDPOINT = "/coinbase-accounts"; 15 | 16 | final CoinbaseExchange coinbaseExchange; 17 | 18 | public PaymentService(final CoinbaseExchange coinbaseExchange) { 19 | this.coinbaseExchange = coinbaseExchange; 20 | } 21 | 22 | public List getPaymentTypes() { 23 | return coinbaseExchange.getAsList(PAYMENT_METHODS_ENDPOINT, new ParameterizedTypeReference(){}); 24 | } 25 | 26 | public List getCoinbaseAccounts() { 27 | return coinbaseExchange.getAsList(COINBASE_ACCOUNTS_ENDPOINT, new ParameterizedTypeReference() {}); 28 | } 29 | } -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/payments/PaymentType.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.payments; 2 | 3 | /** 4 | * Created by robevansuk on 16/02/2017. 5 | */ 6 | public class PaymentType { 7 | private String id; // UUID 8 | private String type; // ach_bank_account 9 | private String name; // Bank of America - eBan... ********7134 10 | private String currency; // USD 11 | private Boolean primary_buy; 12 | private Boolean primary_sell; 13 | private Boolean allow_buy; 14 | private Boolean allow_sell; 15 | private Boolean allow_deposit; 16 | private Boolean allow_withdraw; 17 | private Limit limits; 18 | 19 | public PaymentType() {} 20 | 21 | public PaymentType(String id, 22 | String type, 23 | String name, 24 | String currency, 25 | Boolean primary_buy, 26 | Boolean primary_sell, 27 | Boolean allow_buy, 28 | Boolean allow_sell, 29 | Boolean allow_deposit, 30 | Boolean allow_withdraw, 31 | Limit limits) { 32 | this.id = id; 33 | this.type = type; 34 | this.name = name; 35 | this.currency = currency; 36 | this.primary_buy = primary_buy; 37 | this.primary_sell = primary_sell; 38 | this.allow_buy = allow_buy; 39 | this.allow_sell = allow_sell; 40 | this.allow_deposit = allow_deposit; 41 | this.allow_withdraw = allow_withdraw; 42 | this.limits = limits; 43 | } 44 | 45 | public String getId() { 46 | return id; 47 | } 48 | 49 | public void setId(String id) { 50 | this.id = id; 51 | } 52 | 53 | public String getType() { 54 | return type; 55 | } 56 | 57 | public void setType(String type) { 58 | this.type = type; 59 | } 60 | 61 | public String getName() { 62 | return name; 63 | } 64 | 65 | public void setName(String name) { 66 | this.name = name; 67 | } 68 | 69 | public String getCurrency() { 70 | return currency; 71 | } 72 | 73 | public void setCurrency(String currency) { 74 | this.currency = currency; 75 | } 76 | 77 | public Boolean getPrimary_buy() { 78 | return primary_buy; 79 | } 80 | 81 | public void setPrimary_buy(Boolean primary_buy) { 82 | this.primary_buy = primary_buy; 83 | } 84 | 85 | public Boolean getPrimary_sell() { 86 | return primary_sell; 87 | } 88 | 89 | public void setPrimary_sell(Boolean primary_sell) { 90 | this.primary_sell = primary_sell; 91 | } 92 | 93 | public Boolean getAllow_buy() { 94 | return allow_buy; 95 | } 96 | 97 | public void setAllow_buy(Boolean allow_buy) { 98 | this.allow_buy = allow_buy; 99 | } 100 | 101 | public Boolean getAllow_sell() { 102 | return allow_sell; 103 | } 104 | 105 | public void setAllow_sell(Boolean allow_sell) { 106 | this.allow_sell = allow_sell; 107 | } 108 | 109 | public Boolean getAllow_deposit() { 110 | return allow_deposit; 111 | } 112 | 113 | public void setAllow_deposit(Boolean allow_deposit) { 114 | this.allow_deposit = allow_deposit; 115 | } 116 | 117 | public Boolean getAllow_withdraw() { 118 | return allow_withdraw; 119 | } 120 | 121 | public void setAllow_withdraw(Boolean allow_withdraw) { 122 | this.allow_withdraw = allow_withdraw; 123 | } 124 | 125 | public Limit getLimits() { 126 | return limits; 127 | } 128 | 129 | public void setLimits(Limit limits) { 130 | this.limits = limits; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/payments/SepaDepositInformation.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.payments; 2 | 3 | /** 4 | * Created by robevansuk on 16/02/2017. 5 | */ 6 | public class SepaDepositInformation { 7 | 8 | String iban; 9 | String swift; 10 | String bank_name; 11 | String bank_address; 12 | String bank_country_name; 13 | String account_name; 14 | String account_address; 15 | String reference; 16 | 17 | public SepaDepositInformation() {} 18 | 19 | public SepaDepositInformation(String iban, 20 | String swift, 21 | String bank_name, 22 | String bank_address, 23 | String bank_country_name, 24 | String account_name, 25 | String account_address, 26 | String reference) { 27 | this.iban = iban; 28 | this.swift = swift; 29 | this.bank_name = bank_name; 30 | this.bank_address = bank_address; 31 | this.bank_country_name = bank_country_name; 32 | this.account_name = account_name; 33 | this.account_address = account_address; 34 | this.reference = reference; 35 | } 36 | 37 | public String getIban() { 38 | return iban; 39 | } 40 | 41 | public void setIban(String iban) { 42 | this.iban = iban; 43 | } 44 | 45 | public String getSwift() { 46 | return swift; 47 | } 48 | 49 | public void setSwift(String swift) { 50 | this.swift = swift; 51 | } 52 | 53 | public String getBank_name() { 54 | return bank_name; 55 | } 56 | 57 | public void setBank_name(String bank_name) { 58 | this.bank_name = bank_name; 59 | } 60 | 61 | public String getBank_address() { 62 | return bank_address; 63 | } 64 | 65 | public void setBank_address(String bank_address) { 66 | this.bank_address = bank_address; 67 | } 68 | 69 | public String getBank_country_name() { 70 | return bank_country_name; 71 | } 72 | 73 | public void setBank_country_name(String bank_country_name) { 74 | this.bank_country_name = bank_country_name; 75 | } 76 | 77 | public String getAccount_name() { 78 | return account_name; 79 | } 80 | 81 | public void setAccount_name(String account_name) { 82 | this.account_name = account_name; 83 | } 84 | 85 | public String getAccount_address() { 86 | return account_address; 87 | } 88 | 89 | public void setAccount_address(String account_address) { 90 | this.account_address = account_address; 91 | } 92 | 93 | public String getReference() { 94 | return reference; 95 | } 96 | 97 | public void setReference(String reference) { 98 | this.reference = reference; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/products/ProductService.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.products; 2 | 3 | import com.coinbase.exchange.api.exchange.CoinbaseExchange; 4 | import com.coinbase.exchange.model.Candles; 5 | import com.coinbase.exchange.model.Granularity; 6 | import com.coinbase.exchange.model.Product; 7 | import org.springframework.core.ParameterizedTypeReference; 8 | 9 | import java.time.Instant; 10 | import java.time.format.DateTimeFormatter; 11 | import java.time.format.DateTimeFormatterBuilder; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | import static java.util.stream.Collectors.joining; 17 | 18 | /** 19 | * Created by robevansuk on 03/02/2017. 20 | */ 21 | public class ProductService { 22 | 23 | public static final String PRODUCTS_ENDPOINT = "/products"; 24 | 25 | final CoinbaseExchange exchange; 26 | 27 | public ProductService(final CoinbaseExchange exchange) { 28 | this.exchange = exchange; 29 | } 30 | 31 | // no paged products necessary 32 | public List getProducts() { 33 | return exchange.getAsList(PRODUCTS_ENDPOINT, new ParameterizedTypeReference() { 34 | }); 35 | } 36 | 37 | public Candles getCandles(String productId) { 38 | return new Candles(exchange.get(PRODUCTS_ENDPOINT + "/" + productId + "/candles", new ParameterizedTypeReference>() { 39 | })); 40 | } 41 | 42 | public Candles getCandles(String productId, Map queryParams) { 43 | StringBuffer url = new StringBuffer(PRODUCTS_ENDPOINT + "/" + productId + "/candles"); 44 | if (queryParams != null && queryParams.size() != 0) { 45 | url.append("?"); 46 | url.append(queryParams.entrySet().stream() 47 | .map(entry -> entry.getKey() + "=" + entry.getValue()) 48 | .collect(joining("&"))); 49 | } 50 | return new Candles(exchange.get(url.toString(), new ParameterizedTypeReference>() {})); 51 | } 52 | 53 | /** 54 | * If either one of the start or end fields are not provided then both fields will be ignored. 55 | * If a custom time range is not declared then one ending now is selected. 56 | */ 57 | public Candles getCandles(String productId, Instant startTime, Instant endTime, Granularity granularity) { 58 | 59 | Map queryParams = new HashMap<>(); 60 | 61 | if (startTime != null) { 62 | queryParams.put("start", startTime.toString()); 63 | } 64 | if (endTime != null) { 65 | queryParams.put("end", endTime.toString()); 66 | } 67 | if (granularity != null) { 68 | queryParams.put("granularity", granularity.get()); 69 | } 70 | 71 | return getCandles(productId, queryParams); 72 | } 73 | 74 | /** 75 | * The granularity field must be one of the following values: {60, 300, 900, 3600, 21600, 86400} 76 | */ 77 | public Candles getCandles(String productId, Granularity granularity) { 78 | return getCandles(productId, null, null, granularity); 79 | } 80 | 81 | /** 82 | * If either one of the start or end fields are not provided then both fields will be ignored. 83 | */ 84 | public Candles getCandles(String productId, Instant start, Instant end) { 85 | return getCandles(productId, start, end, null); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/reports/ReportRequest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.reports; 2 | 3 | /** 4 | * Created by robevansuk on 16/02/2017. 5 | */ 6 | public class ReportRequest { 7 | 8 | String type; 9 | String start_date; 10 | String end_date; 11 | 12 | public ReportRequest() {} 13 | 14 | public ReportRequest(String type, String start_date, String end_date) { 15 | this.type = type; 16 | this.start_date = start_date; 17 | this.end_date = end_date; 18 | } 19 | 20 | public String getType() { 21 | return type; 22 | } 23 | 24 | public void setType(String type) { 25 | this.type = type; 26 | } 27 | 28 | public String getStart_date() { 29 | return start_date; 30 | } 31 | 32 | public void setStart_date(String start_date) { 33 | this.start_date = start_date; 34 | } 35 | 36 | public String getEnd_date() { 37 | return end_date; 38 | } 39 | 40 | public void setEnd_date(String end_date) { 41 | this.end_date = end_date; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/reports/ReportResponse.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.reports; 2 | 3 | /** 4 | * Created by robevansuk on 16/02/2017. 5 | */ 6 | public class ReportResponse { 7 | 8 | String id; 9 | String type; 10 | String status; 11 | String created_at; 12 | String completed_at; 13 | String expires_at; 14 | String file_url; 15 | TimePeriod params; 16 | 17 | public ReportResponse() {} 18 | 19 | public ReportResponse(String id, 20 | String type, 21 | String status, 22 | String created_at, 23 | String completed_at, 24 | String expires_at, 25 | String file_url, 26 | TimePeriod params) { 27 | this.id = id; 28 | this.type = type; 29 | this.status = status; 30 | this.created_at = created_at; 31 | this.completed_at = completed_at; 32 | this.expires_at = expires_at; 33 | this.file_url = file_url; 34 | this.params = params; 35 | } 36 | 37 | public String getId() { 38 | return id; 39 | } 40 | 41 | public void setId(String id) { 42 | this.id = id; 43 | } 44 | 45 | public String getType() { 46 | return type; 47 | } 48 | 49 | public void setType(String type) { 50 | this.type = type; 51 | } 52 | 53 | public String getStatus() { 54 | return status; 55 | } 56 | 57 | public void setStatus(String status) { 58 | this.status = status; 59 | } 60 | 61 | public String getCreated_at() { 62 | return created_at; 63 | } 64 | 65 | public void setCreated_at(String created_at) { 66 | this.created_at = created_at; 67 | } 68 | 69 | public String getCompleted_at() { 70 | return completed_at; 71 | } 72 | 73 | public void setCompleted_at(String completed_at) { 74 | this.completed_at = completed_at; 75 | } 76 | 77 | public String getExpires_at() { 78 | return expires_at; 79 | } 80 | 81 | public void setExpires_at(String expires_at) { 82 | this.expires_at = expires_at; 83 | } 84 | 85 | public String getFile_url() { 86 | return file_url; 87 | } 88 | 89 | public void setFile_url(String file_url) { 90 | this.file_url = file_url; 91 | } 92 | 93 | public TimePeriod getParams() { 94 | return params; 95 | } 96 | 97 | public void setParams(TimePeriod params) { 98 | this.params = params; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/reports/ReportService.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.reports; 2 | 3 | import com.coinbase.exchange.api.exchange.CoinbaseExchange; 4 | import org.springframework.core.ParameterizedTypeReference; 5 | 6 | /** 7 | * Created by robevansuk on 16/02/2017. 8 | */ 9 | public class ReportService { 10 | 11 | private static final String REPORTS_ENDPOINT = "/reports"; 12 | 13 | final CoinbaseExchange coinbaseExchange; 14 | 15 | public ReportService(final CoinbaseExchange coinbaseExchange) { 16 | this.coinbaseExchange = coinbaseExchange; 17 | } 18 | 19 | // TODO untested 20 | public ReportResponse createReport(String type, String startDate, String endDate){ 21 | ReportRequest reportRequest = new ReportRequest(type, startDate, endDate); 22 | return coinbaseExchange.post(REPORTS_ENDPOINT, new ParameterizedTypeReference(){}, reportRequest); 23 | } 24 | 25 | // TODO untested 26 | public ReportResponse getReportStatus(String id) { 27 | return coinbaseExchange.get(REPORTS_ENDPOINT + "/" + id, new ParameterizedTypeReference(){}); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/reports/TimePeriod.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.reports; 2 | 3 | /** 4 | * Created by robevansuk on 16/02/2017. 5 | */ 6 | public class TimePeriod { 7 | 8 | String start_date; 9 | String end_date; 10 | 11 | public TimePeriod() {} 12 | 13 | public TimePeriod(String start_date, String end_date) { 14 | this.start_date = start_date; 15 | this.end_date = end_date; 16 | } 17 | 18 | public String getStart_date() { 19 | return start_date; 20 | } 21 | 22 | public void setStart_date(String start_date) { 23 | this.start_date = start_date; 24 | } 25 | 26 | public String getEnd_date() { 27 | return end_date; 28 | } 29 | 30 | public void setEnd_date(String end_date) { 31 | this.end_date = end_date; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/transfers/Transfer.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.transfers; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * Created by robevansuk on 15/02/2017. 7 | */ 8 | public class Transfer { 9 | 10 | String type; // deposit/withdraw 11 | BigDecimal amount; 12 | String coinbase_account_id; 13 | 14 | public Transfer() {} 15 | 16 | public Transfer(String type, BigDecimal amount, String coinbase_account_id) { 17 | this.type = type; 18 | this.amount = amount; 19 | this.coinbase_account_id = coinbase_account_id; 20 | } 21 | 22 | public String getType() { 23 | return type; 24 | } 25 | 26 | public void setType(String type) { 27 | this.type = type; 28 | } 29 | 30 | public BigDecimal getAmount() { 31 | return amount; 32 | } 33 | 34 | public void setAmount(BigDecimal amount) { 35 | this.amount = amount; 36 | } 37 | 38 | public String getCoinbase_account_id() { 39 | return coinbase_account_id; 40 | } 41 | 42 | public void setCoinbase_account_id(String coinbase_account_id) { 43 | this.coinbase_account_id = coinbase_account_id; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/transfers/TransferService.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.transfers; 2 | 3 | import com.coinbase.exchange.api.exchange.CoinbaseExchange; 4 | import org.springframework.core.ParameterizedTypeReference; 5 | 6 | import java.math.BigDecimal; 7 | 8 | /** 9 | * This class is best used in conjunction with the coinbase library 10 | * to get the coinbase account Id's. see: https://github.com/coinbase/coinbase-java 11 | * 12 | * Created by robevansuk on 15/02/2017. 13 | */ 14 | public class TransferService { 15 | 16 | static final String TRANSFER_ENDPOINT = "/transfers"; 17 | 18 | final CoinbaseExchange coinbaseExchange; 19 | 20 | public TransferService(final CoinbaseExchange coinbaseExchange) { 21 | this.coinbaseExchange = coinbaseExchange; 22 | } 23 | 24 | /** 25 | * TODO untested due to lack of a coinbaseaccountID to test with. 26 | * @param type 27 | * @param amount 28 | * @param coinbaseAccountId 29 | * @return 30 | */ 31 | public String transfer(String type, BigDecimal amount, String coinbaseAccountId) { 32 | return coinbaseExchange.post(TRANSFER_ENDPOINT, 33 | new ParameterizedTypeReference(){}, 34 | new Transfer(type, amount, coinbaseAccountId)); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/useraccount/UserAccountData.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.useraccount; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * Created by robevansuk on 17/02/2017. 7 | */ 8 | public class UserAccountData { 9 | 10 | String product_id; 11 | BigDecimal exchange_volume; 12 | BigDecimal volume; 13 | String recorded_at; 14 | 15 | public UserAccountData() {} 16 | 17 | public UserAccountData(String product_id, BigDecimal exchange_volume, BigDecimal volume, String recorded_at) { 18 | this.product_id = product_id; 19 | this.exchange_volume = exchange_volume; 20 | this.volume = volume; 21 | this.recorded_at = recorded_at; 22 | } 23 | 24 | public String getProduct_id() { 25 | return product_id; 26 | } 27 | 28 | public void setProduct_id(String product_id) { 29 | this.product_id = product_id; 30 | } 31 | 32 | public BigDecimal getExchange_volume() { 33 | return exchange_volume; 34 | } 35 | 36 | public void setExchange_volume(BigDecimal exchange_volume) { 37 | this.exchange_volume = exchange_volume; 38 | } 39 | 40 | public BigDecimal getVolume() { 41 | return volume; 42 | } 43 | 44 | public void setVolume(BigDecimal volume) { 45 | this.volume = volume; 46 | } 47 | 48 | public String getRecorded_at() { 49 | return recorded_at; 50 | } 51 | 52 | public void setRecorded_at(String recorded_at) { 53 | this.recorded_at = recorded_at; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/useraccount/UserAccountService.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.useraccount; 2 | 3 | import com.coinbase.exchange.api.exchange.CoinbaseExchange; 4 | import org.springframework.core.ParameterizedTypeReference; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by robevansuk on 17/02/2017. 10 | */ 11 | public class UserAccountService { 12 | 13 | private static final String USER_ACCOUNT_ENDPOINT = "/users/self/trailing-volume"; 14 | 15 | final CoinbaseExchange coinbaseExchange; 16 | 17 | public UserAccountService(final CoinbaseExchange coinbaseExchange) { 18 | this.coinbaseExchange = coinbaseExchange; 19 | } 20 | 21 | /** 22 | * Returns the 30 day trailing volume information from all accounts 23 | * @return UserAccountData 24 | */ 25 | public List getTrailingVolume(){ 26 | return coinbaseExchange.getAsList(USER_ACCOUNT_ENDPOINT, new ParameterizedTypeReference() {}); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /api/src/main/java/com/coinbase/exchange/api/withdrawals/WithdrawalsService.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.withdrawals; 2 | 3 | import com.coinbase.exchange.api.exchange.CoinbaseExchange; 4 | import com.coinbase.exchange.model.CoinbasePaymentRequest; 5 | import com.coinbase.exchange.model.CryptoPaymentRequest; 6 | import com.coinbase.exchange.model.MonetaryRequest; 7 | import com.coinbase.exchange.model.PaymentRequest; 8 | import com.coinbase.exchange.model.PaymentResponse; 9 | import org.springframework.core.ParameterizedTypeReference; 10 | 11 | import java.math.BigDecimal; 12 | import java.math.RoundingMode; 13 | 14 | /** 15 | * Created by robevansuk on 16/02/2017. 16 | */ 17 | public class WithdrawalsService { 18 | 19 | private static final String WITHDRAWALS_ENDPOINT = "/withdrawals"; 20 | private static final String PAYMENT_METHOD = "/payment-method"; 21 | private static final String COINBASE = "/coinbase-account"; 22 | private static final String CRYPTO = "/crypto"; 23 | 24 | final CoinbaseExchange coinbaseExchange; 25 | 26 | public WithdrawalsService(final CoinbaseExchange coinbaseExchange) { 27 | this.coinbaseExchange = coinbaseExchange; 28 | } 29 | 30 | public PaymentResponse makeWithdrawalToPaymentMethod(BigDecimal amount, String currency, String paymentMethodId) { 31 | PaymentRequest request = new PaymentRequest(amount, currency, paymentMethodId); 32 | return makeWithdrawal(request, PAYMENT_METHOD); 33 | } 34 | 35 | // TODO untested - needs coinbase account ID to work. 36 | public PaymentResponse makeWithdrawalToCoinbase(BigDecimal amount, String currency, String coinbaseAccountId) { 37 | CoinbasePaymentRequest request = new CoinbasePaymentRequest(amount.setScale(8, RoundingMode.HALF_DOWN), currency, coinbaseAccountId); 38 | return makeWithdrawal(request, COINBASE); 39 | } 40 | 41 | // TODO untested - needs a crypto currency account address 42 | public PaymentResponse makeWithdrawalToCryptoAccount(BigDecimal amount, String currency, String cryptoAccountAddress) { 43 | CryptoPaymentRequest request = new CryptoPaymentRequest(amount.setScale(8, RoundingMode.HALF_DOWN), currency, cryptoAccountAddress); 44 | return makeWithdrawal(request, CRYPTO); 45 | } 46 | 47 | 48 | private PaymentResponse makeWithdrawal(MonetaryRequest request, String withdrawalType) { 49 | return coinbaseExchange.post(WITHDRAWALS_ENDPOINT+ withdrawalType, 50 | new ParameterizedTypeReference() {}, 51 | request); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /api/src/test/java/com/coinbase/exchange/api/BaseIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api; 2 | 3 | import com.coinbase.exchange.api.exchange.CoinbaseExchange; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | /** 8 | * This is an integration test. Tests extending this class should be 9 | * run it against the Coinbase Pro sandbox API. To do this you will need 10 | * to provide credentials in resources/application-test.yml 11 | * 12 | * Created by robevansuk on 20/01/2017. 13 | */ 14 | @SpringBootTest(properties = { 15 | "spring.profiles.active=test" 16 | }) 17 | public abstract class BaseIntegrationTest { 18 | 19 | @Autowired 20 | public CoinbaseExchange exchange; 21 | } 22 | -------------------------------------------------------------------------------- /api/src/test/java/com/coinbase/exchange/api/TestExchangeApplication.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * The main codebase is a library. In order to test it we can 8 | * instantiate a spring boot application that will wire in the various 9 | * config from application-test.yaml properties and connect to the exchange. 10 | * This makes running integration tests relatively simple. 11 | */ 12 | @SpringBootApplication 13 | public class TestExchangeApplication { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(TestExchangeApplication.class, args); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /api/src/test/java/com/coinbase/exchange/api/accounts/AccountsIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.accounts; 2 | 3 | import com.coinbase.exchange.api.BaseIntegrationTest; 4 | import com.coinbase.exchange.api.config.IntegrationTestConfiguration; 5 | import com.coinbase.exchange.model.Hold; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.ExtendWith; 9 | import org.springframework.context.annotation.Import; 10 | import org.springframework.test.context.junit.jupiter.SpringExtension; 11 | 12 | import java.util.List; 13 | 14 | import static org.junit.jupiter.api.Assertions.assertNotNull; 15 | import static org.junit.jupiter.api.Assertions.assertTrue; 16 | 17 | /** 18 | * See class doc for BaseIntegrationTest 19 | * 20 | * Created by robevansuk on 03/02/2017. 21 | */ 22 | @ExtendWith(SpringExtension.class) 23 | @Import({IntegrationTestConfiguration.class}) 24 | public class AccountsIntegrationTest extends BaseIntegrationTest { 25 | 26 | AccountService accountService; 27 | 28 | @BeforeEach 29 | void setUp() { 30 | this.accountService = new AccountService(exchange); 31 | } 32 | 33 | @Test 34 | public void canGetAccounts() { 35 | List accounts = accountService.getAccounts(); 36 | assertNotNull(accounts); 37 | } 38 | 39 | @Test 40 | public void getAccount() { 41 | List accounts = accountService.getAccounts(); 42 | Account account = accountService.getAccount(accounts.get(0).getId()); 43 | assertNotNull(account); 44 | } 45 | 46 | @Test 47 | public void canGetAccountHistory() { 48 | List accounts = accountService.getAccounts(); 49 | List history = accountService.getAccountHistory(accounts.get(0).getId()); 50 | assertNotNull(history); // anything but null/error. 51 | } 52 | 53 | @Test 54 | public void canGetAccountHolds() { 55 | List accounts = accountService.getAccounts(); 56 | List holds = accountService.getHolds(accounts.get(0).getId()); 57 | assertNotNull(holds); 58 | // only check the holds if they exist 59 | if (holds.size()>0) { 60 | assertTrue(holds.get(0).getAmount().floatValue() >= 0.0f); 61 | } 62 | } 63 | 64 | /** 65 | * note that for paged requests the before param takes precedence 66 | * only if before is null and after is not-null will the after param be inserted. 67 | */ 68 | @Test 69 | public void canGetPagedAccountHistory() { 70 | List accounts = accountService.getAccounts(); 71 | assertTrue(accounts.size() > 0); 72 | String beforeOrAfter = "before"; 73 | int pageNumber = 1; 74 | int limit = 5; 75 | List firstPageAccountHistory = accountService.getPagedAccountHistory(accounts.get(0).getId(), 76 | beforeOrAfter, pageNumber, limit); 77 | assertNotNull(firstPageAccountHistory); 78 | assertTrue(firstPageAccountHistory.size() <= limit); 79 | } 80 | 81 | @Test 82 | public void canGetPagedHolds() { 83 | List accounts = accountService.getAccounts(); 84 | 85 | assertTrue(accounts!=null); 86 | assertTrue(accounts.size() > 0); 87 | 88 | String beforeOrAfter = "after"; 89 | int pageNumber = 1; 90 | int limit = 5; 91 | 92 | List firstPageOfHolds = accountService.getPagedHolds(accounts.get(0).getId(), 93 | beforeOrAfter, 94 | pageNumber, 95 | limit); 96 | 97 | assertNotNull(firstPageOfHolds); 98 | assertTrue(firstPageOfHolds.size() <= limit); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /api/src/test/java/com/coinbase/exchange/api/accounts/DepositIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.accounts; 2 | 3 | import com.coinbase.exchange.api.BaseIntegrationTest; 4 | import com.coinbase.exchange.api.config.IntegrationTestConfiguration; 5 | import com.coinbase.exchange.api.deposits.DepositService; 6 | import com.coinbase.exchange.api.payments.CoinbaseAccount; 7 | import com.coinbase.exchange.api.payments.PaymentService; 8 | import com.coinbase.exchange.model.PaymentResponse; 9 | import org.junit.Ignore; 10 | import org.junit.jupiter.api.BeforeEach; 11 | import org.junit.jupiter.api.Test; 12 | import org.junit.jupiter.api.extension.ExtendWith; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.context.annotation.Import; 16 | import org.springframework.test.context.junit.jupiter.SpringExtension; 17 | 18 | import java.math.BigDecimal; 19 | import java.util.List; 20 | 21 | import static org.junit.jupiter.api.Assertions.assertEquals; 22 | import static org.junit.jupiter.api.Assertions.assertNotNull; 23 | import static org.junit.jupiter.api.Assertions.assertTrue; 24 | 25 | /** 26 | * See class doc for BaseIntegrationTest 27 | */ 28 | @ExtendWith(SpringExtension.class) 29 | @Import({IntegrationTestConfiguration.class}) 30 | @Ignore 31 | public class DepositIntegrationTest extends BaseIntegrationTest { 32 | private final static Logger log = LoggerFactory.getLogger(DepositIntegrationTest.class); 33 | 34 | PaymentService paymentService; 35 | AccountService accountService; 36 | DepositService testee; 37 | 38 | @BeforeEach 39 | void setUp() { 40 | this.paymentService = new PaymentService(exchange); 41 | this.accountService = new AccountService(exchange); 42 | this.testee = new DepositService(exchange); 43 | } 44 | 45 | @Test 46 | public void depositToGDAXExchangeFromCoinbase(){ 47 | // given 48 | List coinbaseAccountList = paymentService.getCoinbaseAccounts(); 49 | assertTrue(coinbaseAccountList.size() > 0); 50 | List gdaxAccountList = accountService.getAccounts(); 51 | assertTrue(gdaxAccountList.size() > 0); 52 | CoinbaseAccount coinbaseAccount = getCoinbaseAccount(coinbaseAccountList); 53 | Account gdaxAccount = getAccount(gdaxAccountList); 54 | log.info("Testing depositToGDAXExchangeFromCoinbase with " + coinbaseAccount.getId()); 55 | 56 | BigDecimal initGDAXValue = gdaxAccount.getBalance(); 57 | BigDecimal depositAmount = new BigDecimal(100); 58 | 59 | PaymentResponse response = testee.depositViaCoinbase(depositAmount, coinbaseAccount.getCurrency(), coinbaseAccount.getId()); 60 | log.info("Returned: " + response.getId()); 61 | 62 | // when 63 | gdaxAccount = accountService.getAccount(gdaxAccount.getId()); 64 | 65 | // then 66 | assertEquals(0, initGDAXValue.add(depositAmount).compareTo(gdaxAccount.getBalance())); 67 | } 68 | 69 | private Account getAccount(List gdaxAccountList) { 70 | Account gdaxAccount = null; 71 | for(Account account: gdaxAccountList){ 72 | if(account.getCurrency().equalsIgnoreCase("USD")){ 73 | gdaxAccount = account; 74 | break; 75 | } 76 | } 77 | assertNotNull(gdaxAccount); 78 | return gdaxAccount; 79 | } 80 | 81 | private CoinbaseAccount getCoinbaseAccount(List coinbaseAccountList) { 82 | CoinbaseAccount coinbaseAccount = null; 83 | for(CoinbaseAccount account : coinbaseAccountList){ 84 | if(account.getCurrency().equalsIgnoreCase("USD")){ 85 | coinbaseAccount = account; 86 | break; 87 | } 88 | } 89 | assertNotNull(coinbaseAccount); 90 | return coinbaseAccount; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /api/src/test/java/com/coinbase/exchange/api/accounts/UserAccountServiceIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.accounts; 2 | 3 | import com.coinbase.exchange.api.BaseIntegrationTest; 4 | import com.coinbase.exchange.api.config.IntegrationTestConfiguration; 5 | import com.coinbase.exchange.api.useraccount.UserAccountData; 6 | import com.coinbase.exchange.api.useraccount.UserAccountService; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | import org.junit.jupiter.api.extension.ExtendWith; 10 | import org.springframework.context.annotation.Import; 11 | import org.springframework.test.context.junit.jupiter.SpringExtension; 12 | 13 | import java.util.List; 14 | 15 | import static org.junit.jupiter.api.Assertions.assertNotNull; 16 | 17 | /** 18 | * See class doc for BaseIntegrationTest 19 | */ 20 | @ExtendWith(SpringExtension.class) 21 | @Import({IntegrationTestConfiguration.class}) 22 | public class UserAccountServiceIntegrationTest extends BaseIntegrationTest { 23 | 24 | UserAccountService userAccountService; 25 | 26 | @BeforeEach 27 | void setUp() { 28 | this.userAccountService = new UserAccountService(exchange); 29 | } 30 | 31 | /** 32 | * Trailing volume could be empty so all we have to do is make sure it's not returning null 33 | */ 34 | @Test 35 | public void getTrailingVolume(){ 36 | List data = userAccountService.getTrailingVolume(); 37 | assertNotNull(data); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /api/src/test/java/com/coinbase/exchange/api/accounts/WithdrawalIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.accounts; 2 | 3 | import com.coinbase.exchange.api.BaseIntegrationTest; 4 | import com.coinbase.exchange.api.config.IntegrationTestConfiguration; 5 | import com.coinbase.exchange.api.payments.CoinbaseAccount; 6 | import com.coinbase.exchange.api.payments.PaymentService; 7 | import com.coinbase.exchange.api.payments.PaymentType; 8 | import com.coinbase.exchange.api.withdrawals.WithdrawalsService; 9 | import com.coinbase.exchange.model.PaymentResponse; 10 | import org.junit.Ignore; 11 | import org.junit.jupiter.api.BeforeEach; 12 | import org.junit.jupiter.api.Test; 13 | import org.junit.jupiter.api.extension.ExtendWith; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | import org.springframework.context.annotation.Import; 17 | import org.springframework.test.context.junit.jupiter.SpringExtension; 18 | 19 | import java.math.BigDecimal; 20 | import java.util.List; 21 | 22 | import static org.junit.jupiter.api.Assertions.assertEquals; 23 | import static org.junit.jupiter.api.Assertions.assertNotNull; 24 | import static org.junit.jupiter.api.Assertions.assertTrue; 25 | 26 | /** 27 | * See class doc for BaseIntegrationTest 28 | */ 29 | @ExtendWith(SpringExtension.class) 30 | @Import({IntegrationTestConfiguration.class}) 31 | @Ignore 32 | public class WithdrawalIntegrationTest extends BaseIntegrationTest { 33 | 34 | private final static Logger log = LoggerFactory.getLogger(WithdrawalIntegrationTest.class); 35 | 36 | PaymentService paymentService; 37 | WithdrawalsService withdrawalsService; 38 | AccountService accountService; 39 | 40 | @BeforeEach 41 | void setUp() { 42 | this.paymentService = new PaymentService(exchange); 43 | this.withdrawalsService = new WithdrawalsService(exchange); 44 | this.accountService = new AccountService(exchange); 45 | } 46 | 47 | @Test 48 | public void withdrawToCoinbaseAccount(){ 49 | List gdaxAccounts = accountService.getAccounts(); 50 | List paymentTypes = paymentService.getPaymentTypes(); 51 | List coinbaseAccounts = paymentService.getCoinbaseAccounts(); 52 | assertTrue(paymentTypes.size() > 0); 53 | 54 | PaymentType mainType = getUsdPaymentType(paymentTypes); 55 | Account gdaxAccount = getUsdAccount(gdaxAccounts); 56 | CoinbaseAccount account = getUsdCoinbaseAccount(coinbaseAccounts); 57 | 58 | log.info("Testing withdrawToPayment with " + mainType.getId()); 59 | 60 | BigDecimal gdaxUSDValue = gdaxAccount.getBalance(); 61 | BigDecimal withdrawAmount = new BigDecimal(100); 62 | PaymentResponse response = withdrawalsService.makeWithdrawalToCoinbase(withdrawAmount, mainType.getCurrency(), account.getId()); 63 | assertTrue(response.getId().length() > 0 && response.getAmount().compareTo(withdrawAmount) == 0); 64 | log.info("Returned: " + response.getId()); 65 | gdaxAccount = accountService.getAccount(gdaxAccount.getId()); 66 | assertEquals(0, gdaxUSDValue.subtract(withdrawAmount).compareTo(gdaxAccount.getBalance())); 67 | } 68 | 69 | private CoinbaseAccount getUsdCoinbaseAccount(List coinbaseAccounts) { 70 | CoinbaseAccount account = null; 71 | for(CoinbaseAccount coinbaseAccount : coinbaseAccounts){ 72 | if(coinbaseAccount.getCurrency().equalsIgnoreCase("USD")){ 73 | account = coinbaseAccount; 74 | } 75 | } 76 | assertNotNull(account); 77 | return account; 78 | } 79 | 80 | private Account getUsdAccount(List gdaxAccounts) { 81 | Account gdaxAccount = null; 82 | for(Account account : gdaxAccounts){ 83 | if(account.getCurrency().equalsIgnoreCase("USD")){ 84 | gdaxAccount = account; 85 | break; 86 | } 87 | } 88 | assertNotNull(gdaxAccount); 89 | return gdaxAccount; 90 | } 91 | 92 | private PaymentType getUsdPaymentType(List paymentTypes) { 93 | PaymentType mainType = null; 94 | for(PaymentType paymentType : paymentTypes){ 95 | if(paymentType.getCurrency().equalsIgnoreCase("USD")){ 96 | mainType = paymentType; 97 | break; 98 | } 99 | } 100 | assertNotNull(mainType); 101 | return mainType; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /api/src/test/java/com/coinbase/exchange/api/authentication/AuthenticationIntegrationIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.authentication; 2 | 3 | import com.coinbase.exchange.api.BaseIntegrationTest; 4 | import com.coinbase.exchange.api.accounts.Account; 5 | import com.coinbase.exchange.api.accounts.AccountService; 6 | import com.coinbase.exchange.api.config.IntegrationTestConfiguration; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | import org.junit.jupiter.api.extension.ExtendWith; 10 | import org.springframework.context.annotation.Import; 11 | import org.springframework.test.context.junit.jupiter.SpringExtension; 12 | 13 | import java.util.List; 14 | 15 | import static org.junit.jupiter.api.Assertions.assertNotNull; 16 | import static org.junit.jupiter.api.Assertions.assertTrue; 17 | 18 | /** 19 | * See class doc for BaseIntegrationTest 20 | * 21 | * Created by irufus (sakamura@gmail.com) 22 | * @Description The primary function of this class is to run through basic tests for the Authentication and GdaxExchange classes 23 | */ 24 | @ExtendWith(SpringExtension.class) 25 | @Import({IntegrationTestConfiguration.class}) 26 | public class AuthenticationIntegrationIntegrationTest extends BaseIntegrationTest { 27 | 28 | AccountService accountService; 29 | 30 | @BeforeEach 31 | void setUp() { 32 | accountService = new AccountService(exchange); 33 | } 34 | 35 | // ensure a basic request can be made. Not a great test. Improve. 36 | @Test 37 | public void simpleAuthenticationTest(){ 38 | List accounts = accountService.getAccounts(); 39 | assertNotNull(accounts); 40 | assertTrue(accounts.size() > 0); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /api/src/test/java/com/coinbase/exchange/api/config/IntegrationTestConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.config; 2 | 3 | import com.coinbase.exchange.api.exchange.CoinbaseExchange; 4 | import com.coinbase.exchange.api.exchange.CoinbaseExchangeImpl; 5 | import com.coinbase.exchange.security.Signature; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.boot.SpringBootConfiguration; 10 | import org.springframework.context.annotation.Bean; 11 | 12 | @SpringBootConfiguration 13 | public class IntegrationTestConfiguration { 14 | 15 | @Bean 16 | public ObjectMapper objectMapper() { 17 | return new ObjectMapper().registerModule(new JavaTimeModule()); 18 | } 19 | 20 | @Bean 21 | public CoinbaseExchange coinbaseExchange(@Value("${exchange.key}") String apiKey, 22 | @Value("${exchange.passphrase}") String passphrase, 23 | @Value("${exchange.api.baseUrl}") String baseUrl, 24 | @Value("${exchange.secret}") String secretKey, 25 | ObjectMapper objectMapper) { 26 | return new CoinbaseExchangeImpl(apiKey, 27 | passphrase, 28 | baseUrl, 29 | new Signature(secretKey), 30 | objectMapper); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /api/src/test/java/com/coinbase/exchange/api/marketdata/MarketDataIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.marketdata; 2 | 3 | import com.coinbase.exchange.api.BaseIntegrationTest; 4 | import com.coinbase.exchange.api.config.IntegrationTestConfiguration; 5 | import com.coinbase.exchange.api.products.ProductService; 6 | import com.coinbase.exchange.model.Product; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | import org.junit.jupiter.api.extension.ExtendWith; 10 | import org.springframework.context.annotation.Import; 11 | import org.springframework.test.context.junit.jupiter.SpringExtension; 12 | 13 | import java.util.List; 14 | 15 | import static org.junit.jupiter.api.Assertions.assertNotNull; 16 | import static org.junit.jupiter.api.Assertions.assertTrue; 17 | 18 | /** 19 | * See class doc for BaseIntegrationTest 20 | * 21 | * Created by robevansuk on 14/02/2017. 22 | */ 23 | @ExtendWith(SpringExtension.class) 24 | @Import({IntegrationTestConfiguration.class}) 25 | public class MarketDataIntegrationTest extends BaseIntegrationTest { 26 | 27 | ProductService productService; 28 | MarketDataService testee; 29 | 30 | @BeforeEach 31 | void setUp() { 32 | productService = new ProductService(exchange); 33 | testee = new MarketDataService(exchange); 34 | } 35 | 36 | @Test 37 | public void canGetMarketDataForLevelOneBidAndAsk() { 38 | MarketData marketData = testee.getMarketDataOrderBook("BTC-GBP", 1); 39 | System.out.println(marketData); 40 | assertTrue(marketData.getSequence() > 0); 41 | } 42 | 43 | @Test 44 | public void canGetMarketDataForLevelTwoBidAndAsk() { 45 | MarketData marketData = testee.getMarketDataOrderBook("BTC-GBP", 2); 46 | System.out.println(marketData); 47 | assertTrue(marketData.getSequence() > 0); 48 | } 49 | 50 | /** 51 | * note that the returned results are slightly different for level 3. For level 3 you will see an 52 | * order Id rather than the count of orders at a certain price. 53 | */ 54 | @Test 55 | public void canGetMarketDataForLevelThreeBidAndAsk() { 56 | MarketData marketData = testee.getMarketDataOrderBook("BTC-GBP", 3); 57 | System.out.println(marketData); 58 | assertTrue(marketData.getSequence() > 0); 59 | } 60 | 61 | @Test 62 | public void canGetLevel1DataForAllProducts(){ 63 | List products = productService.getProducts(); 64 | for(Product product : products){ 65 | System.out.print("\nTesting: " + product.getId()); 66 | MarketData data = testee.getMarketDataOrderBook(product.getId(), 1); 67 | assertNotNull(data); 68 | 69 | if(data.getBids().size() > 0 && data.getAsks().size() > 0) { 70 | System.out.print(" B: " + data.getBids().get(0).getPrice() + " A: " + data.getAsks().get(0).getPrice()); 71 | } else { 72 | System.out.print(" NO DATA "); 73 | } 74 | try { 75 | Thread.sleep(1000); 76 | } catch (InterruptedException e) { 77 | e.printStackTrace(); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /api/src/test/java/com/coinbase/exchange/api/marketdata/OrderItemDeserializerTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.marketdata; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.io.IOException; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | 10 | /** 11 | * Created by ren7881 on 20/03/2017. 12 | */ 13 | public class OrderItemDeserializerTest { 14 | 15 | private ObjectMapper mapper = new ObjectMapper(); 16 | 17 | /** 18 | * This is now out of date - the api delivers a list of strings - amount, price, order Id. NOT number of orders as this test shows. 19 | * @throws IOException 20 | */ 21 | @Test 22 | public void testDesirialization() throws IOException { 23 | String test = "{\n" + 24 | " \"sequence\": \"3\",\n" + 25 | " \"bids\": [\n" + 26 | " [ \"111.96\", \"2.11111\", 3 ],\n" + 27 | " [ \"295.96\", \"4.39088265\", 2 ]\n" + 28 | " ],\n" + 29 | " \"asks\": [\n" + 30 | " [ \"555.97\", \"66.5656565\", 10 ],\n" + 31 | " [ \"295.97\", \"25.23542881\", 12 ]\n" + 32 | " ]\n" + 33 | "}"; 34 | 35 | MarketData marketData = mapper.readValue(test, MarketData.class); 36 | assertEquals(marketData.getAsks().size(), 2); 37 | assertEquals(marketData.getBids().size(), 2); 38 | assertEquals(marketData.getSequence(), 3L); 39 | } 40 | } -------------------------------------------------------------------------------- /api/src/test/java/com/coinbase/exchange/api/payments/PaymentIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.payments; 2 | 3 | import com.coinbase.exchange.api.BaseIntegrationTest; 4 | import com.coinbase.exchange.api.config.IntegrationTestConfiguration; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | import org.junit.jupiter.api.extension.ExtendWith; 8 | import org.springframework.context.annotation.Import; 9 | import org.springframework.test.context.junit.jupiter.SpringExtension; 10 | 11 | import java.util.List; 12 | 13 | import static org.junit.jupiter.api.Assertions.assertTrue; 14 | 15 | @ExtendWith(SpringExtension.class) 16 | @Import({IntegrationTestConfiguration.class}) 17 | public class PaymentIntegrationTest extends BaseIntegrationTest { 18 | 19 | PaymentService testee; 20 | 21 | @BeforeEach 22 | void setUp() { 23 | testee = new PaymentService(exchange); 24 | } 25 | 26 | @Test 27 | public void hasAvailablePayments(){ 28 | List types = testee.getPaymentTypes(); 29 | assertTrue(types.size() > 0); 30 | } 31 | @Test 32 | public void hasCoinbaseAccounts(){ 33 | List accounts = testee.getCoinbaseAccounts(); 34 | assertTrue(accounts.size() > 0); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /api/src/test/java/com/coinbase/exchange/api/products/ProductsIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.products; 2 | 3 | import com.coinbase.exchange.api.BaseIntegrationTest; 4 | import com.coinbase.exchange.api.config.IntegrationTestConfiguration; 5 | import com.coinbase.exchange.model.Candles; 6 | import com.coinbase.exchange.model.Granularity; 7 | import com.coinbase.exchange.model.Product; 8 | import org.junit.jupiter.api.BeforeEach; 9 | import org.junit.jupiter.api.Test; 10 | import org.junit.jupiter.api.extension.ExtendWith; 11 | import org.springframework.context.annotation.Import; 12 | import org.springframework.test.context.junit.jupiter.SpringExtension; 13 | 14 | import java.time.Duration; 15 | import java.time.Instant; 16 | import java.time.temporal.ChronoUnit; 17 | import java.util.List; 18 | 19 | import static org.junit.jupiter.api.Assertions.assertEquals; 20 | import static org.junit.jupiter.api.Assertions.assertTrue; 21 | 22 | /** 23 | * See class doc for BaseIntegrationTest 24 | *

25 | * Created by robevansuk on 08/02/2017. 26 | */ 27 | @ExtendWith(SpringExtension.class) 28 | @Import({IntegrationTestConfiguration.class}) 29 | public class ProductsIntegrationTest extends BaseIntegrationTest { 30 | 31 | private static final String TEST_PRODUCT_ID = "BTC-GBP"; 32 | public static final int ONE_DAY_IN_SECONDS = 60 * 60 * 24; 33 | public static final int SIX_HOURS_IN_SECONDS = 60 * 60 * 6; 34 | public static final int ONE_HOUR_IN_SECONDS = 60 * 60; 35 | public static final int FIFTEEN_MINS_IN_SECONDS = 60 * 15; 36 | public static final int FIVE_MINS_IN_SECONDS = 60 * 5; 37 | public static final int ONE_MIN_IN_SECONDS = 60; 38 | 39 | private ProductService productService; 40 | 41 | @BeforeEach 42 | void setUp() { 43 | this.productService = new ProductService(exchange); 44 | } 45 | 46 | @Test 47 | public void canGetProducts() { 48 | List products = productService.getProducts(); 49 | products.forEach(item -> System.out.println(item.getId())); 50 | assertTrue(products.size() >= 0); 51 | } 52 | 53 | @Test 54 | void shouldGetCandles() { 55 | Candles candles = productService.getCandles(TEST_PRODUCT_ID); 56 | 57 | assertEquals(300, candles.getCandleList().size()); 58 | } 59 | 60 | @Test 61 | void shouldGetCandlesForAGanularityOf_OneDay() { 62 | Candles candles = productService.getCandles(TEST_PRODUCT_ID, Granularity.ONE_DAY); 63 | 64 | assertEquals(300, candles.getCandleList().size()); 65 | assertEquals(ONE_DAY_IN_SECONDS, getDuration(candles).getSeconds()); 66 | } 67 | 68 | @Test 69 | void shouldGetCandlesForAGanularityOf_SixHours() { 70 | Candles candles = productService.getCandles(TEST_PRODUCT_ID, Granularity.SIX_HOURS); 71 | 72 | assertEquals(300, candles.getCandleList().size()); 73 | assertEquals(SIX_HOURS_IN_SECONDS, getDuration(candles).getSeconds()); 74 | } 75 | 76 | @Test 77 | void shouldGetCandlesForAGanularityOf_OneHour() { 78 | Candles candles = productService.getCandles(TEST_PRODUCT_ID, Granularity.ONE_HOUR); 79 | 80 | assertEquals(300, candles.getCandleList().size()); 81 | assertEquals(ONE_HOUR_IN_SECONDS, getDuration(candles).getSeconds()); 82 | } 83 | 84 | @Test 85 | void shouldGetCandlesForAGanularityOf_FifteenMins() { 86 | Candles candles = productService.getCandles(TEST_PRODUCT_ID, Granularity.FIFTEEN_MIN); 87 | 88 | assertEquals(300, candles.getCandleList().size()); 89 | assertEquals(FIFTEEN_MINS_IN_SECONDS, getDuration(candles).getSeconds()); 90 | } 91 | 92 | @Test 93 | void shouldGetCandlesForAGanularityOf_FiveMins() { 94 | Candles candles = productService.getCandles(TEST_PRODUCT_ID, Granularity.FIVE_MIN); 95 | 96 | assertEquals(300, candles.getCandleList().size()); 97 | assertEquals(FIVE_MINS_IN_SECONDS, getDuration(candles).getSeconds()); 98 | } 99 | 100 | @Test 101 | void shouldGetCandlesForAGanularityOf_OneMin() { 102 | Candles candles = productService.getCandles(TEST_PRODUCT_ID, Granularity.ONE_MIN); 103 | 104 | assertEquals(300, candles.getCandleList().size()); 105 | assertEquals(ONE_MIN_IN_SECONDS, getDuration(candles).getSeconds()); 106 | } 107 | 108 | @Test 109 | void shouldGetCandlesForWithAStartAndEndTime() { 110 | Instant startTime = Instant.now().minus(400, ChronoUnit.DAYS); 111 | Instant endTime = Instant.now().minus(100, ChronoUnit.DAYS); 112 | 113 | Candles candles = productService.getCandles(TEST_PRODUCT_ID, startTime, endTime, Granularity.ONE_DAY); 114 | 115 | // can't predict how many candles we'll get back but going back more than 300 days 116 | // doesn't return 300 candles here.. easiest just to assert we get more than 100 back. 117 | assertTrue(candles.getCandleList().size() > 100); 118 | } 119 | 120 | private Duration getDuration(Candles candles) { 121 | Instant candleTime1 = candles.getCandleList().get(0).getTime(); 122 | Instant candleTime2 = candles.getCandleList().get(1).getTime(); 123 | return Duration.between(candleTime2, candleTime1); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /api/src/test/java/com/coinbase/exchange/api/transfers/TransferServiceIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.api.transfers; 2 | 3 | import com.coinbase.exchange.api.BaseIntegrationTest; 4 | import com.coinbase.exchange.api.config.IntegrationTestConfiguration; 5 | import org.junit.Ignore; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.extension.ExtendWith; 8 | import org.springframework.context.annotation.Import; 9 | import org.springframework.test.context.junit.jupiter.SpringExtension; 10 | 11 | /** 12 | * See class doc for BaseIntegrationTest 13 | * 14 | * Created by robevansuk on 15/02/2017. 15 | */ 16 | @ExtendWith(SpringExtension.class) 17 | @Import({IntegrationTestConfiguration.class}) 18 | public class TransferServiceIntegrationTest extends BaseIntegrationTest { 19 | 20 | private TransferService transferService; 21 | 22 | @BeforeEach 23 | void setUp() { 24 | this.transferService = new TransferService(exchange); 25 | } 26 | 27 | 28 | @Ignore 29 | public void canTransferFromCoinbaseAccountToGdax() { 30 | // TODO 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /api/src/test/resources/application-test.yml: -------------------------------------------------------------------------------- 1 | exchange: 2 | api: 3 | baseUrl: "https://api-public.sandbox.pro.coinbase.com" 4 | key: "" 5 | secret: "" 6 | passphrase: "" 7 | 8 | websocket: 9 | baseUrl: "wss://ws-feed-public.sandbox.pro.coinbase.com" 10 | 11 | gui: 12 | enabled: true -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | dependencies { 6 | classpath 'com.github.jengelman.gradle.plugins:shadow:5.1.0' 7 | } 8 | } 9 | 10 | apply plugin: 'com.github.johnrengelman.shadow' 11 | 12 | wrapper { 13 | distributionType = Wrapper.DistributionType.ALL 14 | } 15 | 16 | allprojects { 17 | apply plugin: 'java' 18 | 19 | group = 'com.coinbase.exchange' 20 | description = 'Client for the Coinbase Pro API' 21 | version = '0.11.0' 22 | 23 | repositories { 24 | mavenCentral() 25 | } 26 | 27 | test { 28 | exclude '**/*IntegrationTest*' 29 | } 30 | 31 | /** 32 | * User can run these as optional against the sandbox api exchange 33 | **/ 34 | task integrationTest(type: Test) { 35 | include '**/*IntegrationTest*' 36 | } 37 | } 38 | 39 | subprojects { } 40 | 41 | // apply dependency constraints to all java modules 42 | configure(subprojects.findAll { true }) { 43 | 44 | sourceCompatibility = 1.11 45 | targetCompatibility = 1.11 46 | 47 | dependencies { 48 | constraints { 49 | implementation 'com.fasterxml.jackson.core:jackson-core:2.11.0' 50 | implementation 'com.fasterxml.jackson.core:jackson-databind:2.11.0' 51 | implementation 'com.fasterxml.jackson.core:jackson-annotations:2.11.0' 52 | implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.0' 53 | implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.0' 54 | implementation 'org.slf4j:slf4j-api:1.7.28' 55 | implementation 'ch.qos.logback:logback-classic:1.2.3' 56 | implementation 'ch.qos.logback:logback-core:1.2.3' 57 | implementation 'org.springframework.boot:spring-boot-starter-web:2.2.7.RELEASE' 58 | implementation 'org.springframework:spring-context:5.2.6.RELEASE' 59 | 60 | // -------------------------------------- 61 | testImplementation 'junit:junit:4.12' 62 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2' 63 | testImplementation 'org.junit.jupiter:junit-jupiter-params:5.5.2' 64 | testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.5.2' 65 | testImplementation 'org.junit.platform:junit-platform-suite-api:1.3.2' 66 | 67 | testImplementation 'org.springframework.boot:spring-boot-test:2.2.7.RELEASE' 68 | testImplementation 'org.springframework.boot:spring-boot-starter-test:2.2.7.RELEASE' 69 | 70 | testImplementation 'org.mockito:mockito-core:3.0.0' 71 | testImplementation 'org.mockito:mockito-junit-jupiter:3.0.0' 72 | 73 | testImplementation 'org.assertj:assertj-core:3.13.2' 74 | } 75 | } 76 | } 77 | 78 | dependencies { 79 | compile project(":api") 80 | compile project(":model") 81 | compile project(":security") 82 | compile project(":websocketfeed") 83 | } 84 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irufus/gdax-java/2287988f0063e6c5fddd0244e63d5732c1dfd40c/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jan 20 14:01:00 GMT 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.3.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /model/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation 'com.fasterxml.jackson.core:jackson-core' 3 | implementation 'com.fasterxml.jackson.core:jackson-databind' 4 | implementation 'com.fasterxml.jackson.core:jackson-annotations' 5 | implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8' 6 | } 7 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/Candle.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | import java.math.BigDecimal; 4 | import java.time.Instant; 5 | 6 | public class Candle { 7 | 8 | private Instant time; 9 | private BigDecimal low; 10 | private BigDecimal high; 11 | private BigDecimal open; 12 | private BigDecimal close; 13 | private BigDecimal volume; 14 | 15 | public Candle(String[] entry) { 16 | this(Instant.ofEpochSecond(Long.parseLong(entry[0])), 17 | new BigDecimal(entry[1]), 18 | new BigDecimal(entry[2]), 19 | new BigDecimal(entry[3]), 20 | new BigDecimal(entry[4]), 21 | new BigDecimal(entry[5])); 22 | } 23 | 24 | public Candle(Instant time, BigDecimal low, BigDecimal high, BigDecimal open, BigDecimal close, BigDecimal volume) { 25 | this.time = time; 26 | this.low = low; 27 | this.high = high; 28 | this.open = open; 29 | this.close = close; 30 | this.volume = volume; 31 | } 32 | 33 | public Instant getTime() { 34 | return time; 35 | } 36 | 37 | public BigDecimal getLow() { 38 | return low; 39 | } 40 | 41 | public BigDecimal getHigh() { 42 | return high; 43 | } 44 | 45 | public BigDecimal getOpen() { 46 | return open; 47 | } 48 | 49 | public BigDecimal getClose() { 50 | return close; 51 | } 52 | 53 | public BigDecimal getVolume() { 54 | return volume; 55 | } 56 | 57 | public void setTime(Instant time) { 58 | this.time = time; 59 | } 60 | 61 | public void setLow(BigDecimal low) { 62 | this.low = low; 63 | } 64 | 65 | public void setHigh(BigDecimal high) { 66 | this.high = high; 67 | } 68 | 69 | public void setOpen(BigDecimal open) { 70 | this.open = open; 71 | } 72 | 73 | public void setClose(BigDecimal close) { 74 | this.close = close; 75 | } 76 | 77 | public void setVolume(BigDecimal volume) { 78 | this.volume = volume; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/Candles.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | import java.util.List; 4 | import java.util.stream.Collectors; 5 | 6 | public class Candles { 7 | 8 | List candleList; 9 | 10 | public Candles(List candles) { 11 | this.candleList = candles.stream().map(Candle::new).collect(Collectors.toList()); 12 | } 13 | 14 | public List getCandleList() { 15 | return candleList; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/CoinbasePaymentRequest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * Created by robevansuk on 15/02/2017. 7 | */ 8 | public class CoinbasePaymentRequest extends MonetaryRequest { 9 | 10 | private String coinbase_account_id; 11 | private String payment_method_id; 12 | 13 | public CoinbasePaymentRequest(BigDecimal amount, String currency, String coinbase_account_id) { 14 | super(amount, currency); 15 | this.coinbase_account_id = coinbase_account_id; 16 | this.payment_method_id = coinbase_account_id; //Duplicated field for gdax compliance, I believe 17 | //We could probably remove coinbase_account_id but there are no tests for this specific thing 18 | } 19 | public String getCoinbase_account_id() { 20 | return coinbase_account_id; 21 | } 22 | public void setCoinbase_account_id(String coinbase_account_id) { 23 | this.coinbase_account_id = coinbase_account_id; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/CryptoPaymentRequest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class CryptoPaymentRequest extends MonetaryRequest { 6 | private String crypto_address; 7 | 8 | public CryptoPaymentRequest(BigDecimal amount, String currency, String cryptoAddress) { 9 | super(amount, currency); 10 | this.crypto_address = cryptoAddress; 11 | } 12 | public String getCryptoAddress() { 13 | return crypto_address; 14 | } 15 | public void setCryptoAddress(String cryptoAddress) { 16 | this.crypto_address = cryptoAddress; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/Currency.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | import java.math.BigDecimal; 6 | 7 | @JsonIgnoreProperties(ignoreUnknown = true) 8 | public class Currency { 9 | private String id; 10 | private String name; 11 | private BigDecimal min_size; 12 | private String status; 13 | private String status_message; 14 | private BigDecimal max_precision; 15 | private String[] convertible_to; 16 | private String funding_account_id; 17 | private Object details; 18 | 19 | public Currency() { 20 | } 21 | 22 | public Currency(String id, String name, BigDecimal min_size, String status, String status_message, BigDecimal max_precision, String[] convertible_to, String funding_account_id, String details) { 23 | this(); 24 | this.id = id; 25 | this.name = name; 26 | this.min_size = min_size; 27 | this.status = status; 28 | this.status_message = status_message; 29 | this.max_precision = max_precision; 30 | this.convertible_to = convertible_to; 31 | this.funding_account_id = funding_account_id; 32 | this.details = details; 33 | } 34 | 35 | public String getId() { 36 | return id; 37 | } 38 | 39 | public void setId(String id) { 40 | this.id = id; 41 | } 42 | 43 | public String getName() { 44 | return name; 45 | } 46 | 47 | public void setName(String name) { 48 | this.name = name; 49 | } 50 | 51 | public BigDecimal getMin_size() { 52 | return min_size; 53 | } 54 | 55 | public void setMin_size(BigDecimal min_size) { 56 | this.min_size = min_size; 57 | } 58 | 59 | public String getStatus() { 60 | return status; 61 | } 62 | 63 | public void setStatus(String status) { 64 | this.status = status; 65 | } 66 | 67 | public String getStatus_message() { 68 | return status_message; 69 | } 70 | 71 | public void setStatus_message(String status_message) { 72 | this.status_message = status_message; 73 | } 74 | 75 | public BigDecimal getMax_precision() { 76 | return max_precision; 77 | } 78 | 79 | public void setMax_precision(BigDecimal max_precision) { 80 | this.max_precision = max_precision; 81 | } 82 | 83 | public String[] getConvertible_to() { 84 | return convertible_to; 85 | } 86 | 87 | public void setConvertible_to(String[] convertible_to) { 88 | this.convertible_to = convertible_to; 89 | } 90 | 91 | public String getFunding_account_id() { 92 | return funding_account_id; 93 | } 94 | 95 | public void setFunding_account_id(String funding_account_id) { 96 | this.funding_account_id = funding_account_id; 97 | } 98 | 99 | public Object getDetails() { 100 | return details; 101 | } 102 | 103 | public void setDetails(Object details) { 104 | this.details = details; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/Detail.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | /** 4 | * Created by irufus on 2/25/15. 5 | */ 6 | public class Detail { 7 | private String order_id; 8 | private Integer trade_id; 9 | private String product_id; 10 | 11 | public String getOrder_id() { 12 | return order_id; 13 | } 14 | 15 | public void setOrder_id(String order_id) { 16 | this.order_id = order_id; 17 | } 18 | 19 | public Integer getTrade_id() { 20 | return trade_id; 21 | } 22 | 23 | public void setTrade_id(Integer trade_id) { 24 | this.trade_id = trade_id; 25 | } 26 | 27 | public String getProduct_id() { 28 | return product_id; 29 | } 30 | 31 | public void setProduct_id(String product_id) { 32 | this.product_id = product_id; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/Fill.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * Created by irufus on 2/18/15. 7 | */ 8 | public class Fill { 9 | private Integer trade_id; 10 | private String product_id; 11 | private BigDecimal size; 12 | private String order_id; 13 | private String created_at; 14 | private String liquidity; 15 | private BigDecimal fee; 16 | private Boolean settled; 17 | private String side; 18 | 19 | public String getSide() { 20 | return side; 21 | } 22 | 23 | public void setSide(String side) { 24 | this.side = side; 25 | } 26 | 27 | public Boolean getSettled() { 28 | return settled; 29 | } 30 | 31 | public void setSettled(Boolean settled) { 32 | this.settled = settled; 33 | } 34 | 35 | public BigDecimal getFee() { 36 | return fee; 37 | } 38 | 39 | public void setFee(BigDecimal fee) { 40 | this.fee = fee; 41 | } 42 | 43 | public String getCreated_at() { 44 | return created_at; 45 | } 46 | 47 | public void setCreated_at(String created_at) { 48 | this.created_at = created_at; 49 | } 50 | 51 | public String getLiquidity() { 52 | return liquidity; 53 | } 54 | 55 | public void setLiquidity(String liquidity) { 56 | this.liquidity = liquidity; 57 | } 58 | 59 | public String getOrder_id() { 60 | return order_id; 61 | } 62 | 63 | public void setOrder_id(String order_id) { 64 | this.order_id = order_id; 65 | } 66 | 67 | public BigDecimal getSize() { 68 | return size; 69 | } 70 | 71 | public void setSize(BigDecimal size) { 72 | this.size = size; 73 | } 74 | 75 | public String getProduct_id() { 76 | return product_id; 77 | } 78 | 79 | public void setProduct_id(String product_id) { 80 | this.product_id = product_id; 81 | } 82 | 83 | public Integer getTrade_id() { 84 | return trade_id; 85 | } 86 | 87 | public void setTrade_id(Integer trade_id) { 88 | this.trade_id = trade_id; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/Granularity.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | public enum Granularity { 4 | ONE_DAY("1d"), 5 | SIX_HOURS("6h"), 6 | ONE_HOUR("1h"), 7 | FIFTEEN_MIN("15m"), 8 | FIVE_MIN("5m"), 9 | ONE_MIN("1m"); 10 | 11 | private String granularity; 12 | 13 | Granularity(String granularity) { 14 | this.granularity = granularity; 15 | } 16 | 17 | /** 18 | * The granularity field must be one of the following values: 19 | * {60, 300, 900, 3600, 21600, 86400}. 20 | */ 21 | public String get(){ 22 | switch(granularity) { 23 | case "1d": return "86400"; 24 | case "6h": return "21600"; 25 | case "1h": return "3600"; 26 | case "15m": return "900"; 27 | case "5m": return "300"; 28 | case "1m": return "60"; 29 | } 30 | return ""; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/Hold.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * Created by irufus on 2/18/15. 7 | * Updated by robevansuk on 17/2/17 8 | */ 9 | public class Hold { 10 | String id; 11 | String account_id; 12 | String created_at; 13 | String update_at; 14 | BigDecimal amount; 15 | String type; 16 | String ref; 17 | 18 | public Hold () {} 19 | 20 | public Hold(String id, String account_id, String created_at, String update_at, BigDecimal amount, String type, String ref) { 21 | this.id = id; 22 | this.account_id = account_id; 23 | this.created_at = created_at; 24 | this.update_at = update_at; 25 | this.amount = amount; 26 | this.type = type; 27 | this.ref = ref; 28 | } 29 | 30 | public String getId() { 31 | return id; 32 | } 33 | 34 | public void setId(String id) { 35 | this.id = id; 36 | } 37 | 38 | public String getAccount_id() { 39 | return account_id; 40 | } 41 | 42 | public void setAccount_id(String account_id) { 43 | this.account_id = account_id; 44 | } 45 | 46 | public String getCreated_at() { 47 | return created_at; 48 | } 49 | 50 | public void setCreated_at(String created_at) { 51 | this.created_at = created_at; 52 | } 53 | 54 | public String getUpdate_at() { 55 | return update_at; 56 | } 57 | 58 | public void setUpdate_at(String update_at) { 59 | this.update_at = update_at; 60 | } 61 | 62 | public BigDecimal getAmount() { 63 | return amount; 64 | } 65 | 66 | public void setAmount(BigDecimal amount) { 67 | this.amount = amount; 68 | } 69 | 70 | public String getType() { 71 | return type; 72 | } 73 | 74 | public void setType(String type) { 75 | this.type = type; 76 | } 77 | 78 | public String getRef() { 79 | return ref; 80 | } 81 | 82 | public void setRef(String ref) { 83 | this.ref = ref; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/MonetaryRequest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public abstract class MonetaryRequest { 6 | protected BigDecimal amount; 7 | protected String currency; 8 | 9 | public MonetaryRequest(BigDecimal amount, String currency){ 10 | this.amount = amount; 11 | this.currency = currency; 12 | } 13 | 14 | public BigDecimal getAmount() { 15 | return amount; 16 | } 17 | 18 | public void setAmount(BigDecimal amount) { 19 | this.amount = amount; 20 | } 21 | 22 | public String getCurrency() { 23 | return currency; 24 | } 25 | 26 | public void setCurrency(String currency) { 27 | this.currency = currency; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/NewLimitOrderSingle.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.RoundingMode; 5 | 6 | /** 7 | * Created by irufus on 7/31/15. 8 | */ 9 | public class NewLimitOrderSingle extends NewOrderSingle { 10 | private BigDecimal size; 11 | private BigDecimal price; 12 | private Boolean post_only; 13 | 14 | public NewLimitOrderSingle() {} 15 | 16 | public NewLimitOrderSingle(BigDecimal size, BigDecimal price, Boolean post_only, String product_id) { 17 | this.size = size; 18 | this.price = price; 19 | this.post_only = post_only; 20 | super.setProduct_id(product_id); 21 | } 22 | 23 | public NewLimitOrderSingle(BigDecimal size, BigDecimal price, Boolean post_only, 24 | String clientOid, 25 | String type, 26 | String side, 27 | String product_id, 28 | String stp, 29 | String funds) { 30 | this.size = size; 31 | this.price = price; 32 | this.post_only = post_only; 33 | setClient_oid(clientOid); 34 | setType(type); 35 | setSide(side); 36 | setProduct_id(product_id); 37 | setStp(stp); 38 | setFunds(funds); 39 | } 40 | 41 | public Boolean getPost_only() { 42 | return post_only; 43 | } 44 | 45 | public void setPost_only(Boolean post_only) { 46 | this.post_only = post_only; 47 | } 48 | 49 | public BigDecimal getPrice() { 50 | return price.setScale(8, RoundingMode.HALF_UP); 51 | } 52 | 53 | public void setPrice(BigDecimal price) { 54 | this.price = price; 55 | } 56 | 57 | public BigDecimal getSize() { 58 | return size.setScale(8, RoundingMode.HALF_UP); 59 | } 60 | 61 | public void setSize(BigDecimal size) { 62 | this.size = size; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/NewMarketOrderSingle.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * Created by irufus on 7/31/15. 7 | */ 8 | public class NewMarketOrderSingle extends NewOrderSingle { 9 | 10 | private BigDecimal size; //optional: Desired amount in BTC 11 | 12 | public NewMarketOrderSingle(BigDecimal size) { 13 | this.size = size; 14 | } 15 | 16 | public NewMarketOrderSingle(){ 17 | super.setType("market"); 18 | } 19 | 20 | public BigDecimal getSize() { 21 | return size; 22 | } 23 | 24 | public void setSize(BigDecimal size) { 25 | this.size = size; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/NewOrderSingle.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | /** 4 | * 5 | *

 6 |  * {
 7 |  *     "id": "d0c5340b-6d6c-49d9-b567-48c4bfca13d2",
 8 |  *     "price": "0.10000000",
 9 |  *     "size": "0.01000000",
10 |  *     "product_id": "BTC-USD",
11 |  *     "side": "buy",
12 |  *     "stp": "dc",
13 |  *     "type": "limit",
14 |  *     "time_in_force": "GTC",
15 |  *     "post_only": false,
16 |  *     "created_at": "2016-12-08T20:02:28.53864Z",
17 |  *     "fill_fees": "0.0000000000000000",
18 |  *     "filled_size": "0.00000000",
19 |  *     "executed_value": "0.0000000000000000",
20 |  *     "status": "pending",
21 |  *     "settled": false
22 |  * }
23 |  * 
24 | */ 25 | public abstract class NewOrderSingle { 26 | 27 | private String client_oid; //optional 28 | private String type; //default is limit, other types are market and stop 29 | private String side; 30 | private String product_id; 31 | private String stp; //optional: values are dc, co , cn , cb 32 | private String funds; 33 | 34 | public NewOrderSingle() { 35 | } 36 | 37 | public String getStp() { 38 | return stp; 39 | } 40 | 41 | public void setStp(String stp) { 42 | this.stp = stp; 43 | } 44 | 45 | public String getProduct_id() { 46 | return product_id; 47 | } 48 | 49 | public void setProduct_id(String product_id) { 50 | this.product_id = product_id; 51 | } 52 | 53 | public String getSide() { 54 | return side; 55 | } 56 | 57 | public void setSide(String side) { 58 | this.side = side; 59 | } 60 | 61 | public String getClient_oid() { 62 | return client_oid; 63 | } 64 | 65 | public void setClient_oid(String client_oid) { 66 | this.client_oid = client_oid; 67 | } 68 | 69 | public String getFunds() { 70 | return funds; 71 | } 72 | 73 | public void setFunds(String funds) { 74 | this.funds = funds; 75 | } 76 | 77 | public String getType() { 78 | return type; 79 | } 80 | 81 | public void setType(String type) { 82 | this.type = type; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/PaymentRequest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * Created by robevansuk on 15/02/2017. 7 | */ 8 | public class PaymentRequest extends MonetaryRequest { 9 | 10 | private String payment_method_id; 11 | 12 | public PaymentRequest(BigDecimal amount, String currency, String payment_method_id) { 13 | super(amount, currency); 14 | this.payment_method_id = payment_method_id; 15 | } 16 | 17 | public String getPayment_method_id() { 18 | return payment_method_id; 19 | } 20 | public void setPayment_method_id(String payment_method_id) { 21 | this.payment_method_id = payment_method_id; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/PaymentResponse.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * Created by robevansuk on 15/02/2017. 7 | */ 8 | public class PaymentResponse { 9 | 10 | String id; 11 | BigDecimal amount; 12 | String currency; 13 | String payout_at; 14 | 15 | public PaymentResponse() {} 16 | 17 | public PaymentResponse(String id, BigDecimal amount, String currency, String payout_at) { 18 | this.id = id; 19 | this.amount = amount; 20 | this.currency = currency; 21 | this.payout_at = payout_at; 22 | } 23 | 24 | public String getId() { 25 | return id; 26 | } 27 | 28 | public void setId(String id) { 29 | this.id = id; 30 | } 31 | 32 | public BigDecimal getAmount() { 33 | return amount; 34 | } 35 | 36 | public void setAmount(BigDecimal amount) { 37 | this.amount = amount; 38 | } 39 | 40 | public String getCurrency() { 41 | return currency; 42 | } 43 | 44 | public void setCurrency(String currency) { 45 | this.currency = currency; 46 | } 47 | 48 | public String getPayout_at() { 49 | return payout_at; 50 | } 51 | 52 | public void setPayout_at(String payout_at) { 53 | this.payout_at = payout_at; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/Product.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | import java.math.BigDecimal; 6 | 7 | /** 8 | *
  9 |  *     {
 10 |  *       "id": "BTC-USD",
 11 |  *       "base_currency": "BTC",
 12 |  *       "quote_currency": "USD",
 13 |  *       "base_min_size": "0.001",
 14 |  *       "base_max_size": "280",
 15 |  *       "base_increment": "0.00000001",
 16 |  *       "quote_increment": "0.01",
 17 |  *       "display_name": "BTC/USD",
 18 |  *       "status": "online",
 19 |  *       "margin_enabled": false,
 20 |  *       "status_message": "",
 21 |  *       "min_market_funds": "5",
 22 |  *       "max_market_funds": "1000000",
 23 |  *       "post_only": false,
 24 |  *       "limit_only": false,
 25 |  *       "cancel_only": false,
 26 |  *       "type": "spot"
 27 |  *     }
 28 |  * 
29 | */ 30 | @JsonIgnoreProperties(ignoreUnknown = true) 31 | public class Product { 32 | private String id; 33 | private String base_currency; 34 | private String quote_currency; 35 | private Double base_min_size; 36 | private Double base_max_size; 37 | private Double quote_increment; 38 | private double base_increment; 39 | private String display_name; 40 | private String status; 41 | private Boolean margin_enabled; 42 | private String status_message; 43 | private BigDecimal min_market_funds; 44 | private Integer max_market_funds; 45 | private Boolean post_only; 46 | private Boolean limit_only; 47 | private Boolean cancel_only; 48 | private String type; 49 | 50 | public Double getQuote_increment() { 51 | return quote_increment; 52 | } 53 | 54 | public void setQuote_increment(Double quote_increment) { 55 | this.quote_increment = quote_increment; 56 | } 57 | 58 | public Double getBase_max_size() { 59 | return base_max_size; 60 | } 61 | 62 | public void setBase_max_size(Double base_max_size) { 63 | this.base_max_size = base_max_size; 64 | } 65 | 66 | public Double getBase_min_size() { 67 | return base_min_size; 68 | } 69 | 70 | public void setBase_min_size(Double base_min_size) { 71 | this.base_min_size = base_min_size; 72 | } 73 | 74 | public String getQuote_currency() { 75 | return quote_currency; 76 | } 77 | 78 | public void setQuote_currency(String quote_currency) { 79 | this.quote_currency = quote_currency; 80 | } 81 | 82 | public String getBase_currency() { 83 | return base_currency; 84 | } 85 | 86 | public void setBase_currency(String base_currency) { 87 | this.base_currency = base_currency; 88 | } 89 | 90 | public String getId() { 91 | return id; 92 | } 93 | 94 | public void setId(String id) { 95 | this.id = id; 96 | } 97 | 98 | public void setBase_increment(double base_increment) { 99 | this.base_increment = base_increment; 100 | } 101 | 102 | public double getBase_increment() { 103 | return base_increment; 104 | } 105 | 106 | public void setDisplay_name(String display_name) { 107 | this.display_name = display_name; 108 | } 109 | 110 | public String getDisplay_name() { 111 | return display_name; 112 | } 113 | 114 | public String getStatus() { 115 | return status; 116 | } 117 | 118 | public void setStatus(String status) { 119 | this.status = status; 120 | } 121 | 122 | public Boolean getMargin_enabled() { 123 | return margin_enabled; 124 | } 125 | 126 | public void setMargin_enabled(Boolean margin_enabled) { 127 | this.margin_enabled = margin_enabled; 128 | } 129 | 130 | public String getStatus_message() { 131 | return status_message; 132 | } 133 | 134 | public void setStatus_message(String status_message) { 135 | this.status_message = status_message; 136 | } 137 | 138 | public BigDecimal getMin_market_funds() { 139 | return min_market_funds; 140 | } 141 | 142 | public void setMin_market_funds(BigDecimal min_market_funds) { 143 | this.min_market_funds = min_market_funds; 144 | } 145 | 146 | public Integer getMax_market_funds() { 147 | return max_market_funds; 148 | } 149 | 150 | public void setMax_market_funds(Integer max_market_funds) { 151 | this.max_market_funds = max_market_funds; 152 | } 153 | 154 | public Boolean getPost_only() { 155 | return post_only; 156 | } 157 | 158 | public void setPost_only(Boolean post_only) { 159 | this.post_only = post_only; 160 | } 161 | 162 | public Boolean getLimit_only() { 163 | return limit_only; 164 | } 165 | 166 | public void setLimit_only(Boolean limit_only) { 167 | this.limit_only = limit_only; 168 | } 169 | 170 | public Boolean getCancel_only() { 171 | return cancel_only; 172 | } 173 | 174 | public void setCancel_only(Boolean cancel_only) { 175 | this.cancel_only = cancel_only; 176 | } 177 | 178 | public String getType() { 179 | return type; 180 | } 181 | 182 | public void setType(String type) { 183 | this.type = type; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /model/src/main/java/com/coinbase/exchange/model/ProductOrderBook.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.model; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by irufus on 8/3/15. 7 | */ 8 | public class ProductOrderBook { 9 | 10 | private Integer sequence; 11 | private List> bids; 12 | private List> asks; 13 | 14 | public List> getAsks() { 15 | return asks; 16 | } 17 | 18 | public void setAsks(List> asks) { 19 | this.asks = asks; 20 | } 21 | 22 | public List> getBids() { 23 | return bids; 24 | } 25 | 26 | public void setBids(List> bids) { 27 | this.bids = bids; 28 | } 29 | 30 | public Integer getSequence() { 31 | return sequence; 32 | } 33 | 34 | public void setSequence(Integer sequence) { 35 | this.sequence = sequence; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /security/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | group 'com.coinbase.exchange' 6 | version '0.11.0' 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | testCompile group: 'junit', name: 'junit', version: '4.12' 14 | } 15 | -------------------------------------------------------------------------------- /security/src/main/java/com/coinbase/exchange/security/Signature.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.security; 2 | 3 | import com.coinbase.exchange.security.constants.ExchangeConstants; 4 | 5 | import javax.crypto.Mac; 6 | import javax.crypto.spec.SecretKeySpec; 7 | import javax.management.RuntimeErrorException; 8 | import java.security.InvalidKeyException; 9 | import java.util.Base64; 10 | 11 | /** 12 | * Created by robevansuk on 17/03/2017. 13 | */ 14 | public class Signature { 15 | 16 | private final String secretKey; 17 | 18 | public Signature(final String secretKey) { 19 | this.secretKey = secretKey; 20 | } 21 | 22 | /** 23 | * The CB-ACCESS-SIGN header is generated by creating a sha256 HMAC using 24 | * the base64-decoded secret key on the prehash string for: 25 | * timestamp + method + requestPath + body (where + represents string concatenation) 26 | * and base64-encode the output. 27 | * The timestamp value is the same as the CB-ACCESS-TIMESTAMP header. 28 | * @param requestPath 29 | * @param method 30 | * @param body 31 | * @param timestamp 32 | * @return 33 | */ 34 | public String generate(String requestPath, String method, String body, String timestamp) { 35 | try { 36 | String prehash = timestamp + method.toUpperCase() + requestPath + body; 37 | byte[] secretDecoded = Base64.getDecoder().decode(secretKey); 38 | SecretKeySpec keyspec = new SecretKeySpec(secretDecoded, ExchangeConstants.SHARED_MAC.getAlgorithm()); 39 | Mac sha256 = (Mac) ExchangeConstants.SHARED_MAC.clone(); 40 | sha256.init(keyspec); 41 | return Base64.getEncoder().encodeToString(sha256.doFinal(prehash.getBytes())); 42 | } catch (CloneNotSupportedException | InvalidKeyException e) { 43 | e.printStackTrace(); 44 | throw new RuntimeErrorException(new Error("Cannot set up authentication headers.")); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /security/src/main/java/com/coinbase/exchange/security/constants/ExchangeConstants.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.security.constants; 2 | 3 | import javax.crypto.Mac; 4 | import java.security.NoSuchAlgorithmException; 5 | 6 | /** 7 | * Created by robevansuk on 25/01/2017. 8 | */ 9 | public class ExchangeConstants { 10 | 11 | public static Mac SHARED_MAC; 12 | 13 | static { 14 | try { 15 | SHARED_MAC = Mac.getInstance("HmacSHA256"); 16 | } catch (NoSuchAlgorithmException nsaEx) { 17 | nsaEx.printStackTrace(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'coinbase-pro-java' 2 | 3 | include 'api' 4 | include 'security' 5 | include 'websocketfeed' 6 | include 'model' 7 | include 'security' 8 | 9 | -------------------------------------------------------------------------------- /websocketfeed/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compile project(':model') 3 | compile project(':security') 4 | implementation 'org.springframework.boot:spring-boot-starter-web' 5 | implementation 'com.fasterxml.jackson.core:jackson-core' 6 | implementation 'com.fasterxml.jackson.core:jackson-databind' 7 | implementation 'com.fasterxml.jackson.core:jackson-annotations' 8 | implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8' 9 | implementation 'org.slf4j:slf4j-api' 10 | implementation 'ch.qos.logback:logback-classic' 11 | implementation 'ch.qos.logback:logback-core' 12 | 13 | testImplementation 'org.junit.jupiter:junit-jupiter-engine' 14 | testImplementation 'org.junit.jupiter:junit-jupiter-api' 15 | testImplementation 'org.junit.jupiter:junit-jupiter-params' 16 | testImplementation 'org.junit.jupiter:junit-jupiter-engine' 17 | testImplementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' 18 | } 19 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/ActivateOrderBookMessage.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.math.BigDecimal; 6 | import java.time.Instant; 7 | 8 | /** 9 | * An activate message is sent when a stop order is placed. 10 | * When the stop is triggered the order will be placed and go through the order lifecycle. 11 | * Example: 12 | *
13 |  * {
14 |  *   "type": "activate",
15 |  *   "product_id": "test-product",
16 |  *   "timestamp": "1483736448.299000",
17 |  *   "user_id": "12",
18 |  *   "profile_id": "30000727-d308-cf50-7b1c-c06deb1934fc",
19 |  *   "order_id": "7b52009b-64fd-0a2a-49e6-d8a939753077",
20 |  *   "stop_type": "entry",
21 |  *   "side": "buy",
22 |  *   "stop_price": "80",
23 |  *   "size": "2",
24 |  *   "funds": "50",
25 |  *   "private": true
26 |  * }
27 |  * 
28 | */ 29 | public class ActivateOrderBookMessage extends OrderBookMessage { 30 | private String stop_type; 31 | private BigDecimal stop_price; 32 | private Instant timestamp; 33 | 34 | public ActivateOrderBookMessage() { 35 | setType("activate"); 36 | } 37 | 38 | public ActivateOrderBookMessage(String stop_type, BigDecimal stop_price, Instant timestamp, boolean privateFlag) { 39 | this(); 40 | this.stop_type = stop_type; 41 | this.stop_price = stop_price; 42 | this.timestamp = timestamp; 43 | this.privateFlag = privateFlag; 44 | } 45 | 46 | @JsonProperty("private") 47 | private boolean privateFlag; 48 | 49 | public String getStop_type() { 50 | return stop_type; 51 | } 52 | 53 | public void setStop_type(String stop_type) { 54 | this.stop_type = stop_type; 55 | } 56 | 57 | public BigDecimal getStop_price() { 58 | return stop_price; 59 | } 60 | 61 | public void setStop_price(BigDecimal stop_price) { 62 | this.stop_price = stop_price; 63 | } 64 | 65 | public Instant getTimestamp() { 66 | return timestamp; 67 | } 68 | 69 | public void setTimestamp(Instant timestamp) { 70 | this.timestamp = timestamp; 71 | } 72 | 73 | public boolean isPrivateFlag() { 74 | return privateFlag; 75 | } 76 | 77 | public void setPrivateFlag(boolean privateFlag) { 78 | this.privateFlag = privateFlag; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/ChangedOrderBookMessage.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | /** 4 | * An order has changed. This is the result of self-trade prevention 5 | * adjusting the order size or available funds. Orders can only 6 | * decrease in size or funds. change messages are sent anytime an 7 | * order changes in size; this includes resting orders (open) as 8 | * well as received but not yet open. change messages are also 9 | * sent when a new market order goes through self trade prevention 10 | * and the funds for the market order have changed. 11 | *
12 |  * {
13 |  *     "type": "change",
14 |  *     "time": "2014-11-07T08:19:27.028459Z",
15 |  *     "sequence": 80,
16 |  *     "order_id": "ac928c66-ca53-498f-9c13-a110027a60e8",
17 |  *     "product_id": "BTC-USD",
18 |  *     "new_size": "5.23512",
19 |  *     "old_size": "12.234412",
20 |  *     "price": "400.23",
21 |  *     "side": "sell"
22 |  * }
23 |  * 
24 | * change messages for received but not yet open orders can be 25 | * ignored when building a real-time order book. The side field 26 | * of a change message and price can be used as indicators for whether 27 | * the change message is relevant if building from a level 2 book. 28 | * 29 | * Any change message where the price is null indicates that the change 30 | * message is for a market order. Change messages for limit orders will 31 | * always have a price specified. 32 | *
33 |  * {
34 |  *     "type": "change",
35 |  *     "time": "2014-11-07T08:19:27.028459Z",
36 |  *     "sequence": 80,
37 |  *     "order_id": "ac928c66-ca53-498f-9c13-a110027a60e8",
38 |  *     "product_id": "BTC-USD",
39 |  *     "new_funds": "5.23512",
40 |  *     "old_funds": "12.234412",
41 |  *     "price": "400.23",
42 |  *     "side": "sell"
43 |  * }
44 |  * 
45 | */ 46 | public class ChangedOrderBookMessage extends OrderBookMessage { 47 | 48 | public ChangedOrderBookMessage() { 49 | setType("change"); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/Channel.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | import static com.coinbase.exchange.websocketfeed.ChannelName.full; 7 | import static com.coinbase.exchange.websocketfeed.ChannelName.heartbeat; 8 | import static com.coinbase.exchange.websocketfeed.ChannelName.level2; 9 | import static com.coinbase.exchange.websocketfeed.ChannelName.matches; 10 | import static com.coinbase.exchange.websocketfeed.ChannelName.status; 11 | import static com.coinbase.exchange.websocketfeed.ChannelName.ticker; 12 | import static com.coinbase.exchange.websocketfeed.ChannelName.user; 13 | 14 | public class Channel { 15 | public static final Channel CHANNEL_HEARTBEAT = new Channel(heartbeat, null); 16 | public static final Channel CHANNEL_STATUS = new Channel(status, null); 17 | public static final Channel CHANNEL_TICKER = new Channel(ticker, null); 18 | public static final Channel CHANNEL_LEVEL2 = new Channel(level2, null); 19 | public static final Channel CHANNEL_USER = new Channel(user, null); 20 | public static final Channel CHANNEL_MATCHES = new Channel(matches, null); 21 | public static final Channel CHANNEL_FULL = new Channel(full, null); 22 | 23 | private ChannelName name; 24 | 25 | @JsonProperty("product_ids") 26 | @JsonInclude(JsonInclude.Include.NON_NULL) 27 | private String[] product_ids; 28 | 29 | public Channel() { 30 | } 31 | 32 | public Channel(ChannelName name, String[] product_ids) { 33 | this.name = name; 34 | this.product_ids = product_ids; 35 | } 36 | 37 | public ChannelName getName() { 38 | return name; 39 | } 40 | 41 | public void setName(ChannelName name) { 42 | this.name = name; 43 | } 44 | 45 | public String[] getProduct_ids() { 46 | return product_ids; 47 | } 48 | 49 | public void setProduct_ids(String[] product_ids) { 50 | this.product_ids = product_ids; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/ChannelName.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | public enum ChannelName { 4 | heartbeat, status, ticker, level2, user, matches, full 5 | } 6 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/DoneOrderBookMessage.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | /** 4 | *
 5 |  *  {
 6 |  *    "type": "done",
 7 |  *    "time": "2014-11-07T08:19:27.028459Z",
 8 |  *    "product_id": "BTC-USD",
 9 |  *    "sequence": 10,
10 |  *    "price": "200.2",
11 |  *    "order_id": "d50ec984-77a8-460a-b958-66f114b0de9b",
12 |  *    "reason": "filled", // canceled
13 |  *    "side": "sell",
14 |  *    "remaining_size": "0.2"
15 |  *  }
16 |  * 
17 | */ 18 | public class DoneOrderBookMessage extends OrderBookMessage { 19 | 20 | public DoneOrderBookMessage() { 21 | setType("done"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/ErrorOrderBookMessage.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | /** 4 | * If you send a message that is not recognized or an error 5 | * occurs, the error message will be sent and you will be 6 | * disconnected. 7 | *
 8 |  * {
 9 |  *     "type": "error",
10 |  *     "message": "error message"
11 |  * }
12 |  * 
13 | */ 14 | public class ErrorOrderBookMessage extends FeedMessage { 15 | 16 | private String message; 17 | 18 | public ErrorOrderBookMessage() { 19 | setType("error"); 20 | } 21 | 22 | public ErrorOrderBookMessage(String message) { 23 | this(); 24 | this.message = message; 25 | } 26 | 27 | // @JsonCreator 28 | // public ErrorOrderBookMessage(String type, Long sequence, Instant time, String product_id, String message) { 29 | // super(type, sequence, time, product_id); 30 | // this.message = message; 31 | // } 32 | 33 | public String getMessage() { 34 | return message; 35 | } 36 | 37 | public void setMessage(String message) { 38 | this.message = message; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/FeedMessage.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonSubTypes; 5 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 6 | 7 | import java.time.Instant; 8 | 9 | @JsonIgnoreProperties(ignoreUnknown = true) 10 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 11 | @JsonSubTypes({ 12 | @JsonSubTypes.Type(value = ErrorOrderBookMessage.class, name = "error"), 13 | @JsonSubTypes.Type(value = SubscriptionsMessage.class, name = "subscriptions"), 14 | @JsonSubTypes.Type(value = HeartBeat.class, name = "heartbeat"), 15 | @JsonSubTypes.Type(value = ChangedOrderBookMessage.class, name = "change"), 16 | @JsonSubTypes.Type(value = DoneOrderBookMessage.class, name = "done"), 17 | @JsonSubTypes.Type(value = MatchedOrderBookMessage.class, name = "match"), 18 | @JsonSubTypes.Type(value = MatchedOrderBookMessage.class, name = "last_match"), 19 | @JsonSubTypes.Type(value = OpenedOrderBookMessage.class, name = "open"), 20 | @JsonSubTypes.Type(value = ReceivedOrderBookMessage.class, name = "received"), 21 | @JsonSubTypes.Type(value = TickerMessage.class, name = "ticker"), 22 | @JsonSubTypes.Type(value = ActivateOrderBookMessage.class, name = "activate"), 23 | @JsonSubTypes.Type(value = StatusMessage.class, name = "status"), 24 | @JsonSubTypes.Type(value = SnapshotMessage.class, name = "snapshot"), 25 | @JsonSubTypes.Type(value = L2UpdateMessage.class, name = "l2update"), 26 | }) 27 | public abstract class FeedMessage { 28 | 29 | private String type; // "received" | "open" | "done" | "match" | "change" | "activate" 30 | private Long sequence; 31 | private Instant time; 32 | private String product_id; 33 | 34 | public String getType() { 35 | return type; 36 | } 37 | 38 | public void setType(String type) { 39 | this.type = type; 40 | } 41 | 42 | public Long getSequence() { 43 | return sequence; 44 | } 45 | 46 | public void setSequence(Long sequence) { 47 | this.sequence = sequence; 48 | } 49 | 50 | public Instant getTime() { 51 | return time; 52 | } 53 | 54 | public void setTime(Instant time) { 55 | this.time = time; 56 | } 57 | 58 | public String getProduct_id() { 59 | return product_id; 60 | } 61 | 62 | public void setProduct_id(String product_id) { 63 | this.product_id = product_id; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/HeartBeat.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | /** 4 | * A message sent once a second when heartbeat is turned on. 5 | *
 6 |  * {
 7 |  *     "type": "heartbeat",                   // inherited
 8 |  *     "sequence": 90,                        // inherited
 9 |  *     "last_trade_id": 20,
10 |  *     "product_id": "BTC-USD",               // inherited
11 |  *     "time": "2014-11-07T08:19:28.464459Z"  // inherited
12 |  * }
13 |  * 
14 | */ 15 | public class HeartBeat extends FeedMessage { 16 | 17 | private Long last_trade_id; 18 | 19 | public HeartBeat() { 20 | setType("heartbeat"); 21 | } 22 | 23 | public HeartBeat(Long last_trade_id) { 24 | this(); 25 | this.last_trade_id = last_trade_id; 26 | } 27 | 28 | public Long getLast_trade_id() { 29 | return last_trade_id; 30 | } 31 | 32 | public void setLast_trade_id(Long last_trade_id) { 33 | this.last_trade_id = last_trade_id; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/L2UpdateMessage.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | /** 4 | * Subsequent updates will have the type l2update. 5 | * The changes property of l2updates is an array with [side, price, size] tuples. 6 | * The time property of l2update is the time of the event as recorded by our trading engine. Please note that size is the updated size at that price level, not a delta. A size of "0" indicates the price level can be removed. 7 | * Example: 8 | *
 9 |  * {
10 |  *   "type": "l2update",
11 |  *   "product_id": "BTC-GBP",
12 |  *   "changes": [
13 |  *     [
14 |  *       "buy",
15 |  *       "5454.12",
16 |  *       "0.00000000"
17 |  *     ]
18 |  *   ],
19 |  *   "time": "2020-04-10T15:28:07.393966Z"
20 |  * }
21 |  * 
22 | */ 23 | public class L2UpdateMessage extends FeedMessage { 24 | 25 | private String[][] changes; 26 | 27 | public L2UpdateMessage() { 28 | setType("l2update"); 29 | } 30 | 31 | public L2UpdateMessage(String[][] changes) { 32 | this(); 33 | this.changes = changes; 34 | } 35 | 36 | public String[][] getChanges() { 37 | return changes; 38 | } 39 | 40 | public void setChanges(String[][] changes) { 41 | this.changes = changes; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/MatchedOrderBookMessage.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | /** 4 | * A trade occurred between two orders. The aggressor or taker order is 5 | * the one executing immediately after being received and the maker order 6 | * is a resting order on the book. The side field indicates the maker 7 | * order side. If the side is sell this indicates the maker was a sell 8 | * order and the match is considered an up-tick. A buy side match is a 9 | * down-tick. 10 | *
11 |  *  {
12 |  *    "type": "match",
13 |  *    "trade_id": 10,
14 |  *    "sequence": 50,
15 |  *    "maker_order_id": "ac928c66-ca53-498f-9c13-a110027a60e8",
16 |  *    "taker_order_id": "132fb6ae-456b-4654-b4e0-d681ac05cea1",
17 |  *    "time": "2014-11-07T08:19:27.028459Z",
18 |  *    "product_id": "BTC-USD",
19 |  *    "size": "5.23512",
20 |  *    "price": "400.23",
21 |  *    "side": "sell"
22 |  *  }
23 |  * 
24 | * If authenticated, and you were the taker, the message would also have 25 | * the following fields: 26 | *
27 |  *  taker_user_id: "5844eceecf7e803e259d0365",
28 |  *  user_id: "5844eceecf7e803e259d0365",
29 |  *  taker_profile_id: "765d1549-9660-4be2-97d4-fa2d65fa3352",
30 |  *  profile_id: "765d1549-9660-4be2-97d4-fa2d65fa3352"
31 |  * 
32 | */ 33 | public class MatchedOrderBookMessage extends OrderBookMessage { 34 | 35 | public MatchedOrderBookMessage() { 36 | setType("match"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/OpenedOrderBookMessage.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | /** 4 | *
 5 |  *  {
 6 |  *    "type": "open",
 7 |  *    "time": "2014-11-07T08:19:27.028459Z",
 8 |  *    "product_id": "BTC-USD",
 9 |  *    "sequence": 10,
10 |  *    "order_id": "d50ec984-77a8-460a-b958-66f114b0de9b",
11 |  *    "price": "200.2",
12 |  *    "remaining_size": "1.00",
13 |  *    "side": "sell"
14 |  *  }
15 |  * 
16 | */ 17 | public class OpenedOrderBookMessage extends OrderBookMessage { 18 | 19 | public OpenedOrderBookMessage() { 20 | setType("open"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/ReceivedOrderBookMessage.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | 4 | /** 5 | * A valid order has been received and is now active. This message is 6 | * emitted for every single valid order as soon as the matching engine 7 | * receives it whether it fills immediately or not. 8 | * 9 | * The received message does not indicate a resting order on the order book. 10 | * It simply indicates a new incoming order which as been accepted by the 11 | * matching engine for processing. Received orders may cause match message 12 | * to follow if they are able to begin being filled (taker behavior). 13 | * Self-trade prevention may also trigger change messages to follow if the 14 | * order size needs to be adjusted. Orders which are not fully filled or 15 | * canceled due to self-trade prevention result in an open message and 16 | * become resting orders on the order book. 17 | * 18 | * Market orders (indicated by the order_type field) may have an optional 19 | * funds field which indicates how much quote currency will be used to buy 20 | * or sell. For example, a funds field of 100.00 for the BTC-USD product 21 | * would indicate a purchase of up to 100.00 USD worth of bitcoin. 22 | *
23 |  * {
24 |  *    "type": "received",
25 |  *    "time": "2014-11-07T08:19:27.028459Z",
26 |  *    "product_id": "BTC-USD",
27 |  *    "sequence": 10,
28 |  *    "order_id": "d50ec984-77a8-460a-b958-66f114b0de9b",
29 |  *    "size": "1.34",
30 |  *    "price": "502.1",
31 |  *    "side": "buy",
32 |  *    "order_type": "limit"
33 |  *  }
34 |  *
35 |  *  {
36 |  *    "type": "received",
37 |  *    "time": "2014-11-09T08:19:27.028459Z",
38 |  *    "product_id": "BTC-USD",
39 |  *    "sequence": 12,
40 |  *    "order_id": "dddec984-77a8-460a-b958-66f114b0de9b",
41 |  *    "funds": "3000.234",
42 |  *    "side": "buy",
43 |  *    "order_type": "market"
44 |  *  }
45 |  * 
46 | */ 47 | public class ReceivedOrderBookMessage extends OrderBookMessage { 48 | 49 | public ReceivedOrderBookMessage() { 50 | setType("received"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/SnapshotMessage.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | /** 4 | * A snapshot of the order book 5 | * Example: 6 | *
 7 |  * {
 8 |  *     "type": "snapshot",
 9 |  *     "product_id": "BTC-USD",
10 |  *     "bids": [["10101.10", "0.45054140"]],
11 |  *     "asks": [["10102.55", "0.57753524"]]
12 |  * }
13 |  * 
14 | */ 15 | public class SnapshotMessage extends FeedMessage { 16 | 17 | private String[][] bids; 18 | private String[][] asks; 19 | 20 | public SnapshotMessage() { 21 | setType("snapshot"); 22 | } 23 | 24 | public SnapshotMessage(String[][] bids, String[][] asks) { 25 | this(); 26 | this.bids = bids; 27 | this.asks = asks; 28 | } 29 | 30 | public String[][] getBids() { 31 | return bids; 32 | } 33 | 34 | public void setBids(String[][] bids) { 35 | this.bids = bids; 36 | } 37 | 38 | public String[][] getAsks() { 39 | return asks; 40 | } 41 | 42 | public void setAsks(String[][] asks) { 43 | this.asks = asks; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/StatusMessage.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.coinbase.exchange.model.Currency; 4 | import com.coinbase.exchange.model.Product; 5 | 6 | /** 7 | * The status channel will send all products and currencies on a preset interval. 8 | * Example: 9 | *
10 |  * {
11 |  *     "type": "status",
12 |  *     "products": [
13 |  *         {
14 |  *             "id": "BTC-USD",
15 |  *             "base_currency": "BTC",
16 |  *             "quote_currency": "USD",
17 |  *             "base_min_size": "0.001",
18 |  *             "base_max_size": "70",
19 |  *             "base_increment": "0.00000001",
20 |  *             "quote_increment": "0.01",
21 |  *             "display_name": "BTC/USD",
22 |  *             "status": "online",
23 |  *             "status_message": null,
24 |  *             "min_market_funds": "10",
25 |  *             "max_market_funds": "1000000",
26 |  *             "post_only": false,
27 |  *             "limit_only": false,
28 |  *             "cancel_only": false
29 |  *         }
30 |  *     ],
31 |  *     "currencies": [
32 |  *         {
33 |  *             "id": "USD",
34 |  *             "name": "United States Dollar",
35 |  *             "min_size": "0.01000000",
36 |  *             "status": "online",
37 |  *             "status_message": null,
38 |  *             "max_precision": "0.01",
39 |  *             "convertible_to": ["USDC"],
40 |  *             "details": {}
41 |  *         },
42 |  *         {
43 |  *             "id": "USDC",
44 |  *             "name": "USD Coin",
45 |  *             "min_size": "0.00000100",
46 |  *             "status": "online",
47 |  *             "status_message": null,
48 |  *             "max_precision": "0.000001",
49 |  *             "convertible_to": ["USD"],
50 |  *             "details": {}
51 |  *         },
52 |  *         {
53 |  *             "id": "BTC",
54 |  *             "name": "Bitcoin",
55 |  *             "min_size":" 0.00000001",
56 |  *             "status": "online",
57 |  *             "status_message": null,
58 |  *             "max_precision": "0.00000001",
59 |  *             "convertible_to": []
60 |  *         }
61 |  *     ]
62 |  * }
63 |  * 
64 | */ 65 | public class StatusMessage extends FeedMessage { 66 | 67 | private Product[] products; 68 | private Currency[] currencies; 69 | 70 | public StatusMessage() { 71 | setType("status"); 72 | } 73 | 74 | public StatusMessage(Product[] products, Currency[] currencies) { 75 | this(); 76 | this.products = products; 77 | this.currencies = currencies; 78 | } 79 | 80 | public Product[] getProducts() { 81 | return products; 82 | } 83 | 84 | public void setProducts(Product[] products) { 85 | this.products = products; 86 | } 87 | 88 | public Currency[] getCurrencies() { 89 | return currencies; 90 | } 91 | 92 | public void setCurrencies(Currency[] currencies) { 93 | this.currencies = currencies; 94 | } 95 | } -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/Subscribe.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import static com.coinbase.exchange.websocketfeed.ChannelName.full; 4 | 5 | /** 6 | * To begin receiving feed messages, you must first send a subscribe message 7 | * indicating which channels and products to receive. 8 | * Example: 9 | * Subscribe to ETH-USD and ETH-EUR with the level2, heartbeat and ticker channels, 10 | * plus receive the ticker entries for ETH-BTC and ETH-USD 11 | *
12 |  * {
13 |  *   "type": "subscribe",
14 |  *   "product_ids": [
15 |  *     "ETH-USD",
16 |  *     "ETH-EUR"
17 |  *   ],
18 |  *   "channels": [
19 |  *     "level2",
20 |  *     "heartbeat",
21 |  *     {
22 |  *       "name": "ticker",
23 |  *       "product_ids": [
24 |  *         "ETH-BTC",
25 |  *         "ETH-USD"
26 |  *       ]
27 |  *     }
28 |  *   ]
29 |  * }
30 |  * 
31 | * 32 | * See docs https://docs.pro.coinbase.com/#subscribe 33 | */ 34 | public class Subscribe { 35 | 36 | private final static String SUBSCRIBE_MSG_TYPE = "subscribe"; 37 | 38 | private String type = SUBSCRIBE_MSG_TYPE; 39 | private String[] product_ids; 40 | private Channel[] channels; 41 | 42 | // Used for signing the subscribe message to the Websocket feed 43 | private String signature; 44 | private String passphrase; 45 | private String timestamp; 46 | private String apiKey; 47 | 48 | public Subscribe() { } 49 | 50 | public Subscribe(String[] product_ids) { 51 | this.product_ids = product_ids; 52 | this.channels = new Channel[]{new Channel(full, product_ids)}; 53 | } 54 | 55 | public String getType() { 56 | return type; 57 | } 58 | 59 | public void setType(String type) { 60 | this.type = type; 61 | } 62 | 63 | public String[] getProduct_ids() { 64 | return product_ids; 65 | } 66 | 67 | public void setProduct_ids(String[] product_ids) { 68 | this.product_ids = product_ids; 69 | } 70 | 71 | public Subscribe setSignature(String signature) { 72 | this.signature = signature; 73 | return this; 74 | } 75 | 76 | public Subscribe setPassphrase(String passphrase) { 77 | this.passphrase = passphrase; 78 | return this; 79 | } 80 | 81 | public Subscribe setTimestamp(String timestamp) { 82 | this.timestamp = timestamp; 83 | return this; 84 | } 85 | 86 | public Subscribe setKey(String apiKey) { 87 | this.apiKey = apiKey; 88 | return this; 89 | } 90 | 91 | public void setChannels(Channel[] channels) { 92 | this.channels = channels; 93 | } 94 | 95 | public Channel[] getChannels() { 96 | return channels; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/SubscriptionsMessage.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | /** 4 | *
 5 |  * {
 6 |  *     "type": "subscriptions",
 7 |  *     "channels": [
 8 |  *         {
 9 |  *             "name": "level2",
10 |  *             "product_ids": [
11 |  *                 "ETH-USD",
12 |  *                 "ETH-EUR"
13 |  *             ],
14 |  *         },
15 |  *         {
16 |  *             "name": "heartbeat",
17 |  *             "product_ids": [
18 |  *                 "ETH-USD",
19 |  *                 "ETH-EUR"
20 |  *             ],
21 |  *         },
22 |  *         {
23 |  *             "name": "ticker",
24 |  *             "product_ids": [
25 |  *                 "ETH-USD",
26 |  *                 "ETH-EUR",
27 |  *                 "ETH-BTC"
28 |  *             ]
29 |  *         }
30 |  *     ]
31 |  * }
32 |  * 
33 | */ 34 | public class SubscriptionsMessage extends FeedMessage { 35 | 36 | private Channel[] channels; 37 | 38 | 39 | 40 | public Channel[] getChannels() { 41 | return channels; 42 | } 43 | 44 | public void setChannels(Channel[] channels) { 45 | this.channels = channels; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/TickerMessage.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import java.math.BigDecimal; 4 | import java.time.Instant; 5 | 6 | /** 7 | * The ticker channel provides real-time price updates every time a match happens 8 | *
  9 |  * {
 10 |  *     "type": "ticker",
 11 |  *     "trade_id": 20153558,
 12 |  *     "sequence": 3262786978,
 13 |  *     "time": "2017-09-02T17:05:49.250000Z",
 14 |  *     "product_id": "BTC-USD",
 15 |  *     "price": "4388.01000000",
 16 |  *     "side": "buy", // Taker side
 17 |  *     "last_size": "0.03000000",
 18 |  *     "best_bid": "4388",
 19 |  *     "best_ask": "4388.01"
 20 |  * }
 21 |  * 
22 | */ 23 | public class TickerMessage extends FeedMessage { 24 | 25 | private Long trade_id; 26 | private Long sequence; 27 | private Instant time; 28 | private String product_id; 29 | private BigDecimal price; 30 | private String side; 31 | private BigDecimal last_size; 32 | private BigDecimal best_bid; 33 | private BigDecimal best_ask; 34 | 35 | public TickerMessage() { 36 | setType("ticker"); 37 | } 38 | 39 | public TickerMessage(Long trade_id, 40 | Long sequence, 41 | Instant time, 42 | String product_id, 43 | BigDecimal price, 44 | String side, 45 | BigDecimal last_size, 46 | BigDecimal best_bid, 47 | BigDecimal best_ask) { 48 | this(); 49 | this.trade_id = trade_id; 50 | this.sequence = sequence; 51 | this.time = time; 52 | this.product_id = product_id; 53 | this.price = price; 54 | this.side = side; 55 | this.last_size = last_size; 56 | this.best_bid = best_bid; 57 | this.best_ask = best_ask; 58 | } 59 | 60 | public Long getTrade_id() { 61 | return trade_id; 62 | } 63 | 64 | public void setTrade_id(Long trade_id) { 65 | this.trade_id = trade_id; 66 | } 67 | 68 | @Override 69 | public Long getSequence() { 70 | return sequence; 71 | } 72 | 73 | @Override 74 | public void setSequence(Long sequence) { 75 | this.sequence = sequence; 76 | } 77 | 78 | @Override 79 | public Instant getTime() { 80 | return time; 81 | } 82 | 83 | @Override 84 | public void setTime(Instant time) { 85 | this.time = time; 86 | } 87 | 88 | @Override 89 | public String getProduct_id() { 90 | return product_id; 91 | } 92 | 93 | @Override 94 | public void setProduct_id(String product_id) { 95 | this.product_id = product_id; 96 | } 97 | 98 | public BigDecimal getPrice() { 99 | return price; 100 | } 101 | 102 | public void setPrice(BigDecimal price) { 103 | this.price = price; 104 | } 105 | 106 | public String getSide() { 107 | return side; 108 | } 109 | 110 | public void setSide(String side) { 111 | this.side = side; 112 | } 113 | 114 | public BigDecimal getLast_size() { 115 | return last_size; 116 | } 117 | 118 | public void setLast_size(BigDecimal last_size) { 119 | this.last_size = last_size; 120 | } 121 | 122 | public BigDecimal getBest_bid() { 123 | return best_bid; 124 | } 125 | 126 | public void setBest_bid(BigDecimal best_bid) { 127 | this.best_bid = best_bid; 128 | } 129 | 130 | public BigDecimal getBest_ask() { 131 | return best_ask; 132 | } 133 | 134 | public void setBest_ask(BigDecimal best_ask) { 135 | this.best_ask = best_ask; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/WebsocketFeed.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.coinbase.exchange.security.Signature; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import javax.websocket.ClientEndpoint; 11 | import javax.websocket.CloseReason; 12 | import javax.websocket.ContainerProvider; 13 | import javax.websocket.OnClose; 14 | import javax.websocket.OnError; 15 | import javax.websocket.OnMessage; 16 | import javax.websocket.OnOpen; 17 | import javax.websocket.Session; 18 | import javax.websocket.WebSocketContainer; 19 | import java.net.URI; 20 | import java.time.Instant; 21 | import java.util.List; 22 | 23 | /** 24 | * Websocketfeed adapted from someone else's code 25 | *

26 | * >Jiji Sasidharan 27 | */ 28 | @ClientEndpoint 29 | public class WebsocketFeed { 30 | 31 | private static final Logger log = LoggerFactory.getLogger(WebsocketFeed.class); 32 | 33 | private final String websocketUrl; 34 | private final String passphrase; 35 | private final String key; 36 | private final boolean guiEnabled; 37 | private final Signature signature; 38 | 39 | private final ObjectMapper objectMapper; 40 | private Session userSession; 41 | private MessageHandler messageHandler; 42 | 43 | public WebsocketFeed(final String websocketUrl, 44 | final String key, 45 | final String passphrase, 46 | final boolean guiEnabled, 47 | final Signature signature, 48 | final ObjectMapper objectMapper) { 49 | this.key = key; 50 | this.passphrase = passphrase; 51 | this.websocketUrl = websocketUrl; 52 | this.signature = signature; 53 | this.guiEnabled = guiEnabled; 54 | this.objectMapper = objectMapper.registerModule(new JavaTimeModule());; 55 | } 56 | 57 | public void connect() { 58 | if (guiEnabled) { 59 | try { 60 | WebSocketContainer container = ContainerProvider.getWebSocketContainer(); 61 | container.setDefaultMaxBinaryMessageBufferSize(1024 * 1024); 62 | container.setDefaultMaxTextMessageBufferSize(1024 * 1024); 63 | container.connectToServer(this, new URI(websocketUrl)); 64 | } catch (Exception e) { 65 | log.error("Could not connect to remote server", e); 66 | } 67 | } 68 | } 69 | 70 | /** 71 | * Callback hook for Connection open events. 72 | * 73 | * @param userSession the userSession which is opened. 74 | */ 75 | @OnOpen 76 | public void onOpen(Session userSession) { 77 | log.info("opened websocket"); 78 | this.userSession = userSession; 79 | } 80 | 81 | /** 82 | * Callback hook for Connection close events. 83 | * 84 | * @param userSession the userSession which is getting closed. 85 | * @param reason the reason for connection close 86 | */ 87 | @OnClose 88 | public void onClose(Session userSession, CloseReason reason) { 89 | log.info("closing websocket {}", reason); 90 | this.userSession = null; 91 | } 92 | 93 | @OnError 94 | public void onError(Throwable t) { 95 | log.error("websocket error", t); 96 | } 97 | 98 | 99 | /** 100 | * Callback hook for OrderBookMessage Events. This method will be invoked when a client send a message. 101 | * 102 | * @param message The text message 103 | */ 104 | @OnMessage 105 | public void onMessage(String message) { 106 | if (this.messageHandler != null) { 107 | this.messageHandler.handleMessage(message); 108 | } 109 | } 110 | 111 | /** 112 | * register message handler 113 | * 114 | * @param msgHandler 115 | */ 116 | public void addMessageHandler(MessageHandler msgHandler) { 117 | this.messageHandler = msgHandler; 118 | } 119 | 120 | /** 121 | * Send a message. 122 | * 123 | * @param message 124 | */ 125 | public void sendMessage(String message) { 126 | log.info("send: " + message); 127 | this.userSession.getAsyncRemote().sendText(message); 128 | } 129 | 130 | 131 | public void subscribe(Subscribe msg) { 132 | String jsonSubscribeMessage = signObject(msg); 133 | log.info(jsonSubscribeMessage); 134 | // send subscription message to websocket 135 | sendMessage(jsonSubscribeMessage); 136 | } 137 | 138 | // TODO - get this into postHandle interceptor. 139 | public String signObject(Subscribe jsonObj) { 140 | String jsonString = toJson(jsonObj); 141 | 142 | String timestamp = Instant.now().getEpochSecond() + ""; 143 | jsonObj.setKey(key); 144 | jsonObj.setTimestamp(timestamp); 145 | jsonObj.setPassphrase(passphrase); 146 | jsonObj.setSignature(signature.generate("", "GET", jsonString, timestamp)); 147 | 148 | return toJson(jsonObj); 149 | } 150 | 151 | private String toJson(Object object) { 152 | try { 153 | String json = objectMapper.writeValueAsString(object); 154 | return json; 155 | } catch (JsonProcessingException e) { 156 | log.error("Unable to serialize", e); 157 | throw new RuntimeException("Unable to serialize"); 158 | } 159 | } 160 | 161 | 162 | public List getOrdersAfter(Long sequenceId) { 163 | return messageHandler.getQueuedMessages(sequenceId); 164 | } 165 | 166 | 167 | /** 168 | * OrderBookMessage handler. 169 | * 170 | * @author Jiji_Sasidharan 171 | */ 172 | public interface MessageHandler { 173 | public void handleMessage(String message); 174 | 175 | List getQueuedMessages(Long sequenceId); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/WebsocketMessageHandler.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.io.IOException; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.concurrent.CompletableFuture; 11 | 12 | import static java.util.stream.Collectors.toList; 13 | 14 | public class WebsocketMessageHandler implements WebsocketFeed.MessageHandler { 15 | 16 | private static final String FILLED = "filled"; 17 | private static final Logger log = LoggerFactory.getLogger(WebsocketMessageHandler.class); 18 | 19 | private final ObjectMapper objectMapper; 20 | private final List orderMessageQueue; 21 | 22 | public WebsocketMessageHandler(final ObjectMapper objectMapper){ 23 | this.objectMapper = objectMapper; 24 | this.orderMessageQueue = new ArrayList<>(); 25 | } 26 | 27 | @Override 28 | public void handleMessage(final String json) { 29 | CompletableFuture.runAsync(() -> { 30 | FeedMessage message = getObject(json, FeedMessage.class); 31 | 32 | if (message instanceof OrderBookMessage) { 33 | orderMessageQueue.add((OrderBookMessage) message); 34 | } else { 35 | log.warn("IGNORED MESSAGE: {}", message); 36 | } 37 | 38 | if (message instanceof HeartBeat) { 39 | handleHeartbeat((HeartBeat) message); 40 | 41 | } else if (message instanceof ReceivedOrderBookMessage) { 42 | handleReceived(json); 43 | 44 | } else if (message instanceof OpenedOrderBookMessage) { 45 | handleOpened((OpenedOrderBookMessage) message, json); 46 | 47 | } else if (message instanceof DoneOrderBookMessage) { 48 | handleDone((DoneOrderBookMessage) message, json); 49 | 50 | } else if (message instanceof MatchedOrderBookMessage) { 51 | handleMatched(json); 52 | 53 | } else if (message instanceof ChangedOrderBookMessage) { 54 | handleChanged(json); 55 | 56 | } else if (message instanceof ErrorOrderBookMessage) { 57 | handleError((ErrorOrderBookMessage) message, json); 58 | 59 | } else { 60 | log.error("Unsupported message type {}", json); 61 | } 62 | }); 63 | } 64 | 65 | @Override 66 | public List getQueuedMessages(Long sequenceId) { 67 | return orderMessageQueue.stream().filter(msg -> msg.getSequence().compareTo(sequenceId)<0).collect(toList()); 68 | } 69 | 70 | private void handleError(ErrorOrderBookMessage message, String json) { 71 | // Not sure this is required unless I'm attempting to place orders 72 | // ERROR 73 | log.warn("Error {}", json); 74 | ErrorOrderBookMessage errorMessage = message; 75 | } 76 | 77 | private void handleChanged(String json) { 78 | // TODO - possibly need to provide implementation for this to work in real time. 79 | log.info("CHANGED {}", json); 80 | } 81 | 82 | private void handleMatched(String json) { 83 | log.info("MATCHED: {}", json); 84 | OrderBookMessage matchedOrder = getObject(json, MatchedOrderBookMessage.class); 85 | } 86 | 87 | private void handleDone(DoneOrderBookMessage message, String json) { 88 | log.info("DONE: {}", json); 89 | DoneOrderBookMessage doneOrder = message; 90 | } 91 | 92 | private void handleOpened(OpenedOrderBookMessage message, String json) { 93 | log.info("OPENED: {}", json); 94 | OpenedOrderBookMessage openOrderBookMessage = message; 95 | } 96 | 97 | private void handleReceived(String json) { 98 | // received orders are not necessarily live orders - 99 | // so safe to ignore these msgs as they're unnecessary 100 | // https://docs.pro.coinbase.com/#the-full-channel see here for more details 101 | 102 | } 103 | 104 | private void handleHeartbeat(HeartBeat message) { 105 | HeartBeat heartBeat = message; 106 | log.info("HEARTBEAT. Last trade id: {}", heartBeat.getLast_trade_id()); 107 | } 108 | 109 | 110 | public T getObject(String json, Class type) { 111 | try { 112 | return objectMapper.readValue(json, type); 113 | } catch (IOException e) { 114 | log.error("Parsing {} Failed: {}", type, e); 115 | } 116 | return null; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/ActivateOrderBookMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.math.BigDecimal; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | import static org.junit.jupiter.api.Assertions.assertTrue; 12 | 13 | 14 | class ActivateOrderBookMessageTest { 15 | 16 | @Test 17 | void shouldDeserialiseActivateMessagesFromJsonSuccessfully() throws JsonProcessingException { 18 | ObjectMapper objectMapper = new ObjectMapper(); 19 | objectMapper.registerModule(new JavaTimeModule()); 20 | String json = "{" + 21 | " \"type\": \"activate\"," + 22 | " \"product_id\": \"BTC-GBP\"," + 23 | " \"timestamp\": \"1483736448.299000\"," + 24 | " \"user_id\": \"12\"," + 25 | " \"profile_id\": \"30000727-d308-cf50-7b1c-c06deb1934fc\"," + 26 | " \"order_id\": \"7b52009b-64fd-0a2a-49e6-d8a939753077\"," + 27 | " \"stop_type\": \"entry\"," + 28 | " \"side\": \"buy\"," + 29 | " \"stop_price\": \"80\"," + 30 | " \"size\": \"2\"," + 31 | " \"funds\": \"50\"," + 32 | " \"private\": true" + 33 | "}"; 34 | 35 | ActivateOrderBookMessage result = objectMapper.readValue(json, ActivateOrderBookMessage.class); 36 | 37 | assertEquals("activate", result.getType()); 38 | assertEquals("BTC-GBP", result.getProduct_id()); 39 | assertEquals(1483736448, result.getTimestamp().getEpochSecond()); 40 | assertEquals(299000000, result.getTimestamp().getNano()); 41 | assertEquals("12", result.getUser_id()); 42 | assertEquals("30000727-d308-cf50-7b1c-c06deb1934fc", result.getProfile_id()); 43 | assertEquals("7b52009b-64fd-0a2a-49e6-d8a939753077", result.getOrder_id()); 44 | assertEquals("entry", result.getStop_type()); 45 | assertEquals("buy", result.getSide()); 46 | assertEquals(new BigDecimal(80), result.getStop_price()); 47 | assertEquals(new BigDecimal(2), result.getSize()); 48 | assertEquals(new BigDecimal(50), result.getFunds()); 49 | assertTrue(result.isPrivateFlag()); 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/ChangedOrderBookMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.math.BigDecimal; 9 | import java.math.RoundingMode; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertEquals; 12 | 13 | class ChangedOrderBookMessageTest { 14 | 15 | @Test 16 | void shouldDeserialiseChangedMessagesFromJsonSuccessfully() throws JsonProcessingException { 17 | ObjectMapper objectMapper = new ObjectMapper(); 18 | objectMapper.registerModule(new JavaTimeModule()); 19 | String json = "{" + 20 | " \"type\": \"change\"," + 21 | " \"time\": \"2014-11-07T08:19:27.028459Z\"," + 22 | " \"sequence\": 80," + 23 | " \"order_id\": \"ac928c66-ca53-498f-9c13-a110027a60e8\"," + 24 | " \"product_id\": \"BTC-USD\"," + 25 | " \"new_funds\": \"5.23512\"," + 26 | " \"old_funds\": \"12.234412\"," + 27 | " \"price\": \"400.23\"," + 28 | " \"side\": \"sell\"" + 29 | "}"; 30 | 31 | ChangedOrderBookMessage result = objectMapper.readValue(json, ChangedOrderBookMessage.class); 32 | 33 | assertEquals("change", result.getType()); 34 | assertEquals("2014-11-07T08:19:27.028459Z", result.getTime().toString()); 35 | assertEquals(80L, result.getSequence()); 36 | assertEquals("ac928c66-ca53-498f-9c13-a110027a60e8", result.getOrder_id()); 37 | assertEquals("BTC-USD", result.getProduct_id()); 38 | assertEquals(new BigDecimal(5.23512).setScale(5, RoundingMode.HALF_UP), result.getNew_funds()); 39 | assertEquals(new BigDecimal(12.234412).setScale(6, RoundingMode.HALF_UP), result.getOld_funds()); 40 | assertEquals(new BigDecimal(400.23).setScale(2, RoundingMode.HALF_UP), result.getPrice()); 41 | assertEquals("sell", result.getSide()); 42 | } 43 | } -------------------------------------------------------------------------------- /websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/DoneOrderBookMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.math.BigDecimal; 9 | import java.math.RoundingMode; 10 | 11 | import static org.junit.jupiter.api.Assertions.*; 12 | 13 | class DoneOrderBookMessageTest { 14 | 15 | @Test 16 | void shouldDeserialiseDoneMessagesFromJsonSuccessfully() throws JsonProcessingException { 17 | ObjectMapper objectMapper = new ObjectMapper(); 18 | objectMapper.registerModule(new JavaTimeModule()); 19 | String json = " {" + 20 | " \"type\": \"done\"," + 21 | " \"time\": \"2014-11-07T08:19:27.028459Z\"," + 22 | " \"product_id\": \"BTC-USD\"," + 23 | " \"sequence\": 10," + 24 | " \"price\": \"200.2\"," + 25 | " \"order_id\": \"d50ec984-77a8-460a-b958-66f114b0de9b\"," + 26 | " \"reason\": \"filled\", " + // canceled 27 | " \"side\": \"sell\"," + 28 | " \"remaining_size\": \"0.2\"" + 29 | " }"; 30 | 31 | DoneOrderBookMessage result = objectMapper.readValue(json, DoneOrderBookMessage.class); 32 | 33 | assertEquals("done", result.getType()); 34 | assertEquals("2014-11-07T08:19:27.028459Z", result.getTime().toString()); 35 | assertEquals("BTC-USD", result.getProduct_id()); 36 | assertEquals(10L, result.getSequence()); 37 | assertEquals(new BigDecimal(200.2).setScale(1, RoundingMode.HALF_UP), result.getPrice()); 38 | assertEquals("d50ec984-77a8-460a-b958-66f114b0de9b", result.getOrder_id()); 39 | assertEquals("filled", result.getReason()); 40 | assertEquals("sell", result.getSide()); 41 | assertEquals(new BigDecimal(0.2).setScale(1, RoundingMode.HALF_UP), result.getRemaining_size()); 42 | } 43 | } -------------------------------------------------------------------------------- /websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/ErrorOrderBookMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertEquals; 8 | 9 | class ErrorOrderBookMessageTest { 10 | 11 | ObjectMapper objectMapper = new ObjectMapper(); 12 | 13 | @Test 14 | void shouldDeserialiseAllFieldsForErrorOrderBookMessage() throws JsonProcessingException { 15 | String json = "{ \"type\": \"error\", \"message\": \"error message\" }"; 16 | 17 | ErrorOrderBookMessage errorMessage = objectMapper.readValue(json, ErrorOrderBookMessage.class); 18 | 19 | assertEquals("error", errorMessage.getType()); 20 | assertEquals("error message", errorMessage.getMessage()); 21 | 22 | } 23 | 24 | 25 | 26 | } -------------------------------------------------------------------------------- /websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/HeartBeatTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | 11 | class HeartBeatTest { 12 | 13 | @Test 14 | void shouldDeserialiseHeartbeatMessageSuccessfully() throws JsonProcessingException { 15 | ObjectMapper objectMapper = new ObjectMapper(); 16 | objectMapper.registerModule(new JavaTimeModule()); 17 | String json = "{" + 18 | " \"type\": \"heartbeat\", " + 19 | " \"sequence\": 90, " + 20 | " \"last_trade_id\": 20," + 21 | " \"product_id\": \"BTC-USD\", " + 22 | " \"time\": \"2014-11-07T08:19:28.464459Z\"" + 23 | "}"; 24 | 25 | HeartBeat result = objectMapper.readValue(json, HeartBeat.class); 26 | 27 | assertEquals("heartbeat", result.getType()); 28 | assertEquals(90L, result.getSequence()); 29 | assertEquals(20, result.getLast_trade_id()); 30 | assertEquals("BTC-USD", result.getProduct_id()); 31 | assertEquals("2014-11-07T08:19:28.464459Z", result.getTime().toString()); 32 | 33 | } 34 | } -------------------------------------------------------------------------------- /websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/L2UpdateMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | 10 | class L2UpdateMessageTest { 11 | 12 | @Test 13 | void shouldDeserialiseL2UpdateMessagesSuccessfully() throws JsonProcessingException { 14 | ObjectMapper objectMapper = new ObjectMapper(); 15 | objectMapper.registerModule(new JavaTimeModule()); 16 | String json = "{ \"type\": \"l2update\"," + 17 | " \"product_id\": \"BTC-GBP\"," + 18 | " \"changes\": [" + 19 | " [" + 20 | " \"buy\"," + 21 | " \"5454.12\"," + 22 | " \"0.00000000\"" + 23 | " ]" + 24 | " ]," + 25 | " \"time\": \"2020-04-10T15:28:07.393966Z\"" + 26 | "}"; 27 | 28 | L2UpdateMessage result = objectMapper.readValue(json, L2UpdateMessage.class); 29 | 30 | assertEquals("l2update", result.getType()); 31 | assertEquals("BTC-GBP", result.getProduct_id()); 32 | assertEquals("buy", result.getChanges()[0][0]); 33 | assertEquals("5454.12", result.getChanges()[0][1]); 34 | assertEquals("0.00000000", result.getChanges()[0][2]); 35 | assertEquals("2020-04-10T15:28:07.393966Z", result.getTime().toString()); 36 | } 37 | } -------------------------------------------------------------------------------- /websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/MatchedOrderBookMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.math.BigDecimal; 9 | import java.math.RoundingMode; 10 | 11 | import static org.junit.jupiter.api.Assertions.*; 12 | 13 | class MatchedOrderBookMessageTest { 14 | 15 | @Test 16 | void shouldDeserialiseMatchedMessagesFromJsonSuccessfully() throws JsonProcessingException { 17 | ObjectMapper objectMapper = new ObjectMapper(); 18 | objectMapper.registerModule(new JavaTimeModule()); 19 | String json = " {" + 20 | " \"type\": \"match\"," + 21 | " \"trade_id\": 10," + 22 | " \"sequence\": 50," + 23 | " \"maker_order_id\": \"ac928c66-ca53-498f-9c13-a110027a60e8\"," + 24 | " \"taker_order_id\": \"132fb6ae-456b-4654-b4e0-d681ac05cea1\"," + 25 | " \"time\": \"2014-11-07T08:19:27.028459Z\"," + 26 | " \"product_id\": \"BTC-USD\"," + 27 | " \"size\": \"5.23512\"," + 28 | " \"price\": \"400.23\"," + 29 | " \"side\": \"sell\"" + 30 | " }"; 31 | 32 | MatchedOrderBookMessage result = objectMapper.readValue(json, MatchedOrderBookMessage.class); 33 | 34 | assertEquals("match", result.getType()); 35 | assertEquals("10", result.getTrade_id()); 36 | assertEquals(50L, result.getSequence()); 37 | assertEquals("ac928c66-ca53-498f-9c13-a110027a60e8", result.getMaker_order_id()); 38 | assertEquals("132fb6ae-456b-4654-b4e0-d681ac05cea1", result.getTaker_order_id()); 39 | assertEquals("2014-11-07T08:19:27.028459Z", result.getTime().toString()); 40 | assertEquals("BTC-USD", result.getProduct_id()); 41 | assertEquals(new BigDecimal(5.23512).setScale(5, RoundingMode.HALF_UP), result.getSize()); 42 | assertEquals(new BigDecimal(400.23).setScale(2, RoundingMode.HALF_UP), result.getPrice()); 43 | assertEquals("sell", result.getSide()); 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/OpenedOrderBookMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.math.BigDecimal; 9 | import java.math.RoundingMode; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertEquals; 12 | 13 | class OpenedOrderBookMessageTest { 14 | 15 | @Test 16 | void shouldDeserialiseOpenedMessagesFromJsonSuccessfully() throws JsonProcessingException { 17 | ObjectMapper objectMapper = new ObjectMapper(); 18 | objectMapper.registerModule(new JavaTimeModule()); 19 | String json = "{" + 20 | " \"type\": \"open\"," + 21 | " \"time\": \"2014-11-07T08:19:27.028459Z\"," + 22 | " \"product_id\": \"BTC-USD\"," + 23 | " \"sequence\": 10," + 24 | " \"order_id\": \"d50ec984-77a8-460a-b958-66f114b0de9b\"," + 25 | " \"price\": \"200.2\"," + 26 | " \"remaining_size\": \"1.00\"," + 27 | " \"side\": \"sell\"" + 28 | "}"; 29 | 30 | OpenedOrderBookMessage result = objectMapper.readValue(json, OpenedOrderBookMessage.class); 31 | 32 | assertEquals("open", result.getType()); 33 | assertEquals("2014-11-07T08:19:27.028459Z", result.getTime().toString()); 34 | assertEquals("BTC-USD", result.getProduct_id()); 35 | assertEquals(10L, result.getSequence()); 36 | assertEquals("d50ec984-77a8-460a-b958-66f114b0de9b", result.getOrder_id()); 37 | assertEquals(new BigDecimal(1.00).setScale(2, RoundingMode.HALF_UP), result.getRemaining_size()); 38 | assertEquals(new BigDecimal(200.2).setScale(1, RoundingMode.HALF_UP), result.getPrice()); 39 | assertEquals("sell", result.getSide()); 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/ReceivedOrderBookMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.math.BigDecimal; 9 | import java.math.RoundingMode; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertEquals; 12 | 13 | class ReceivedOrderBookMessageTest { 14 | 15 | @Test 16 | void shouldDeserialiseReceivedMessagesFromJsonSuccessfully() throws JsonProcessingException { 17 | ObjectMapper objectMapper = new ObjectMapper(); 18 | objectMapper.registerModule(new JavaTimeModule()); 19 | String json = "{" + 20 | " \"type\": \"received\"," + 21 | " \"time\": \"2014-11-07T08:19:27.028459Z\"," + 22 | " \"product_id\": \"BTC-USD\"," + 23 | " \"sequence\": 10," + 24 | " \"order_id\": \"d50ec984-77a8-460a-b958-66f114b0de9b\"," + 25 | " \"size\": \"1.34\"," + 26 | " \"price\": \"502.1\"," + 27 | " \"side\": \"buy\"," + 28 | " \"order_type\": \"limit\"" + 29 | " }"; 30 | 31 | ReceivedOrderBookMessage result = objectMapper.readValue(json, ReceivedOrderBookMessage.class); 32 | 33 | assertEquals("received", result.getType()); 34 | assertEquals("2014-11-07T08:19:27.028459Z", result.getTime().toString()); 35 | assertEquals("BTC-USD", result.getProduct_id()); 36 | assertEquals(10L, result.getSequence()); 37 | assertEquals("d50ec984-77a8-460a-b958-66f114b0de9b", result.getOrder_id()); 38 | assertEquals(new BigDecimal(1.34).setScale(2, RoundingMode.HALF_UP), result.getSize()); 39 | assertEquals(new BigDecimal(502.1).setScale(1, RoundingMode.HALF_UP), result.getPrice()); 40 | assertEquals("buy", result.getSide()); 41 | assertEquals("limit", result.getOrder_type()); 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/SnapshotMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | 10 | class SnapshotMessageTest { 11 | 12 | @Test 13 | void shouldDeserialiseJsonSuccessfullyForValidJson() throws JsonProcessingException { 14 | ObjectMapper objectMapper = new ObjectMapper(); 15 | String json = "{" + 16 | " \"type\": \"snapshot\"," + 17 | " \"product_id\": \"BTC-USD\"," + 18 | " \"bids\": [[\"10101.10\", \"0.45054140\"]]," + 19 | " \"asks\": [[\"10102.55\", \"0.57753524\"]]" + 20 | "}"; 21 | 22 | SnapshotMessage result = objectMapper.readValue(json, SnapshotMessage.class); 23 | 24 | assertEquals("snapshot", result.getType()); 25 | assertEquals("BTC-USD", result.getProduct_id()); 26 | assertArrayEquals(new String[][] { new String[]{"10101.10", "0.45054140"}}, result.getBids()); 27 | assertArrayEquals(new String[][] { new String[]{"10102.55", "0.57753524"}}, result.getAsks()); 28 | } 29 | } -------------------------------------------------------------------------------- /websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/StatusMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.math.BigDecimal; 9 | import java.util.Collections; 10 | 11 | import static java.math.RoundingMode.HALF_UP; 12 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 13 | import static org.junit.jupiter.api.Assertions.assertEquals; 14 | 15 | class StatusMessageTest { 16 | 17 | @Test 18 | void shouldDeserialiseSuccessfullyFromValidJson() throws JsonProcessingException { 19 | ObjectMapper objectMapper = new ObjectMapper(); 20 | objectMapper.registerModule(new JavaTimeModule()); 21 | String json = "{" + 22 | " \"type\": \"status\"," + 23 | " \"products\": [" + 24 | " {" + 25 | " \"id\": \"BTC-USD\"," + 26 | " \"base_currency\": \"BTC\"," + 27 | " \"quote_currency\": \"USD\"," + 28 | " \"base_min_size\": \"0.001\"," + 29 | " \"base_max_size\": \"70\"," + 30 | " \"base_increment\": \"0.00000001\"," + 31 | " \"quote_increment\": \"0.01\"," + 32 | " \"display_name\": \"BTC/USD\"," + 33 | " \"status\": \"online\"," + 34 | " \"status_message\": \"some status message1\"," + 35 | " \"min_market_funds\": \"10\"," + 36 | " \"max_market_funds\": \"1000000\"," + 37 | " \"post_only\": false," + 38 | " \"limit_only\": false," + 39 | " \"cancel_only\": false" + 40 | " }" + 41 | " ]," + 42 | " \"currencies\": [" + 43 | " {" + 44 | " \"id\": \"USD\"," + 45 | " \"name\": \"United States Dollar\"," + 46 | " \"min_size\": \"0.01000000\"," + 47 | " \"status\": \"online\"," + 48 | " \"status_message\": \"some status message2\"," + 49 | " \"max_precision\": \"0.01\"," + 50 | " \"convertible_to\": [\"USDC\"]," + 51 | " \"details\": {}" + 52 | " }," + 53 | " {" + 54 | " \"id\": \"USDC\"," + 55 | " \"name\": \"USD Coin\"," + 56 | " \"min_size\": \"0.00000100\"," + 57 | " \"status\": \"online\"," + 58 | " \"status_message\": null," + 59 | " \"max_precision\": \"0.000001\"," + 60 | " \"convertible_to\": [\"USD\"], " + 61 | " \"details\": {}" + 62 | " }," + 63 | " {" + 64 | " \"id\": \"BTC\"," + 65 | " \"name\": \"Bitcoin\"," + 66 | " \"min_size\":\" 0.00000001\"," + 67 | " \"status\": \"online\"," + 68 | " \"status_message\": null," + 69 | " \"max_precision\": \"0.00000001\"," + 70 | " \"convertible_to\": []" + 71 | " }" + 72 | " ]" + 73 | "}"; 74 | 75 | StatusMessage result = objectMapper.readValue(json, StatusMessage.class); 76 | 77 | assertEquals("status", result.getType()); 78 | 79 | assertEquals("BTC-USD", result.getProducts()[0].getId()); 80 | assertEquals("BTC", result.getProducts()[0].getBase_currency()); 81 | assertEquals("USD", result.getProducts()[0].getQuote_currency()); 82 | assertEquals(0.001d, result.getProducts()[0].getBase_min_size()); 83 | assertEquals(70d, result.getProducts()[0].getBase_max_size()); 84 | assertEquals(0.00000001d, result.getProducts()[0].getBase_increment()); 85 | assertEquals(0.01d, result.getProducts()[0].getQuote_increment()); 86 | assertEquals("BTC/USD", result.getProducts()[0].getDisplay_name()); 87 | assertEquals("online", result.getProducts()[0].getStatus()); 88 | assertEquals("some status message1", result.getProducts()[0].getStatus_message()); 89 | assertEquals(10, result.getProducts()[0].getMin_market_funds()); 90 | assertEquals(1000000, result.getProducts()[0].getMax_market_funds()); 91 | assertEquals(false, result.getProducts()[0].getPost_only()); 92 | assertEquals(false, result.getProducts()[0].getLimit_only()); 93 | assertEquals(false, result.getProducts()[0].getCancel_only()); 94 | 95 | assertEquals("USD", result.getCurrencies()[0].getId()); 96 | assertEquals("United States Dollar", result.getCurrencies()[0].getName()); 97 | assertEquals(new BigDecimal(0.01000000).setScale(8, HALF_UP), result.getCurrencies()[0].getMin_size()); 98 | assertEquals("online", result.getCurrencies()[0].getStatus()); 99 | assertEquals("some status message2", result.getCurrencies()[0].getStatus_message()); 100 | assertEquals(new BigDecimal(0.01).setScale(2, HALF_UP), result.getCurrencies()[0].getMax_precision()); 101 | assertArrayEquals(new String[]{"USDC"}, result.getCurrencies()[0].getConvertible_to()); 102 | assertEquals(Collections.EMPTY_MAP, result.getCurrencies()[0].getDetails()); 103 | 104 | 105 | assertEquals("USDC", result.getCurrencies()[1].getId()); 106 | assertEquals("USD Coin", result.getCurrencies()[1].getName()); 107 | assertEquals(new BigDecimal(0.00000100).setScale(8, HALF_UP), result.getCurrencies()[1].getMin_size()); 108 | assertEquals("online", result.getCurrencies()[1].getStatus()); 109 | assertEquals(null, result.getCurrencies()[1].getStatus_message()); 110 | assertEquals(new BigDecimal(0.000001).setScale(6, HALF_UP), result.getCurrencies()[1].getMax_precision()); 111 | assertArrayEquals(new String[]{"USD"}, result.getCurrencies()[1].getConvertible_to()); 112 | assertEquals(Collections.EMPTY_MAP, result.getCurrencies()[1].getDetails()); 113 | 114 | assertEquals("BTC", result.getCurrencies()[2].getId()); 115 | assertEquals("Bitcoin", result.getCurrencies()[2].getName()); 116 | assertEquals( new BigDecimal(0.00000001).setScale(8, HALF_UP), result.getCurrencies()[2].getMin_size()); 117 | assertEquals("online", result.getCurrencies()[2].getStatus()); 118 | assertEquals(null, result.getCurrencies()[2].getStatus_message()); 119 | assertEquals(new BigDecimal(0.00000001).setScale(8, HALF_UP), result.getCurrencies()[2].getMax_precision()); 120 | assertArrayEquals(new String[]{}, result.getCurrencies()[2].getConvertible_to()); 121 | assertEquals(null, result.getCurrencies()[2].getDetails()); 122 | } 123 | } -------------------------------------------------------------------------------- /websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/TickerMessageTest.java: -------------------------------------------------------------------------------- 1 | package com.coinbase.exchange.websocketfeed; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.math.BigDecimal; 9 | 10 | import static java.math.RoundingMode.HALF_UP; 11 | import static org.junit.jupiter.api.Assertions.assertEquals; 12 | 13 | class TickerMessageTest { 14 | 15 | @Test 16 | void shouldDeserialiseSuccessfullyFromValidJson() throws JsonProcessingException { 17 | ObjectMapper objectMapper = new ObjectMapper(); 18 | objectMapper.registerModule(new JavaTimeModule()); 19 | String json = "{" + 20 | " \"type\": \"ticker\"," + 21 | " \"trade_id\": 20153558," + 22 | " \"sequence\": 3262786978," + 23 | " \"time\": \"2017-09-02T17:05:49.250000Z\"," + 24 | " \"product_id\": \"BTC-USD\"," + 25 | " \"price\": \"4388.01000000\"," + 26 | " \"side\": \"buy\"," + 27 | " \"last_size\": \"0.03000000\"," + 28 | " \"best_bid\": \"4388\"," + 29 | " \"best_ask\": \"4388.01\"" + 30 | "}"; 31 | 32 | TickerMessage result = objectMapper.readValue(json, TickerMessage.class); 33 | 34 | assertEquals("ticker", result.getType()); 35 | assertEquals(20153558L, result.getTrade_id()); 36 | assertEquals(3262786978L, result.getSequence()); 37 | assertEquals("2017-09-02T17:05:49.250Z", result.getTime().toString()); 38 | assertEquals("BTC-USD", result.getProduct_id()); 39 | assertEquals(new BigDecimal(4388.01000000).setScale(8, HALF_UP), result.getPrice()); 40 | assertEquals("buy", result.getSide()); 41 | assertEquals(new BigDecimal(4388).setScale(0, HALF_UP), result.getBest_bid()); 42 | assertEquals(new BigDecimal(4388.01).setScale(2, HALF_UP), result.getBest_ask()); 43 | 44 | 45 | } 46 | } --------------------------------------------------------------------------------