[
  {
    "path": ".github/workflows/build.yaml",
    "content": "name: build\non: [push]\n\njobs:\n  byz-build:\n    name: Coinbase Pro Build\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - name: set up JDK\n        uses: actions/setup-java@v1\n        with:\n          java-version: 11\n      - name: Build (non-master branch)\n        if: github.ref != 'refs/heads/master'\n        run: ./gradlew build"
  },
  {
    "path": ".gitignore",
    "content": "*.iml\n.idea/\n.gradle/\n*.log\n**/classes/\n**/build/\n**/out/\n**/application.*.yml"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nIf 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.\n\nJoin us on the Gitter.im channel (chat link at the top of the main README.md file)\n\nIf 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.\n\nPlease 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).\n\nPlease also update/amend the README.md as necessary regarding changes so that it is kept up to date.\n\n# TESTING\n\nTests act as documentation for the code, demonstrating actual usage.\nIf you're not familiar with TDD then now is a great time to begin!\nTest Driven Development will:\n- help determine what to build\n- help you to prioritise which elements need to be in place first\n- help you to implement a solution using the minimal amount of code\n- frontload the stress of the design of your code, which should emerge from/be driven by the tests\n\nNot testing code leads to poor implementations that are hard to read, debug and are typically unmaintainable.\n\nIf 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.\n\nCurrently tests follow a given/when/then approach. That is:\n- `given` some setup and preconditions\n- `when` I invoke method X on some object under test\n- `then` some results should be expected.\n\nYou'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.\n\nTest naming - typically tests are named in the following way: `shouldDoXWhenY`.\n\nIf you spot a bug, write a test for it to highlight the issue, create a fix, and submit a pull request for review.\n\nCode that has very little in the way of testing is more likely to be rejected.\n\nThe current codebase tests what is sensible and possible to test. Mainly getting things.\n\n## I want to create some new element in the codebase\n\nWhere do you start?\n\nAdd this template, add the relevant tests you want, then begin implementing your code.\n\nNote 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. \nIf a class only has one constructor the `@Autowired` annotation is not necessary.\n\n```java\n@Component\npublic class NewComponent {\n\n    @Autowired\n    public NewComponent() {\n        // put object into a consistent state ready for interacting with.\n    }\n}\n```\n\nIf you want to utilise an existing service, you just need the configuration similar to below:\n\n```java\n@SpringBootConfiguration\npublic class ApplicationConfiguration {\n\n    @Bean\n    public ObjectMapper objectMapper() {\n        ObjectMapper objectMapper = new ObjectMapper();\n        objectMapper.registerModule(new JavaTimeModule());\n        return objectMapper;\n    }\n\n    @Bean\n    public Signature signature(@Value(\"${exchange.secret}\") String secret) {\n        return new Signature(secret);\n    }\n    \n    @Bean\n    public CoinbaseExchange coinbasePro(@Value(\"${exchange.key}\") String publicKey,\n                                        @Value(\"${exchange.passphrase}\") String passphrase,\n                                        @Value(\"${exchange.baseUrl}\") String baseUrl,\n                                        Signature signature,\n                                        ObjectMapper objectMapper) {\n        return new CoinbaseExchangeImpl(publicKey, passphrase, baseUrl, signature, objectMapper);\n    }\n\n   /**\n    * now you can create services by wiring in the CoinbaseExchange object to handle incoming/outgoing requests.\n    **/\n\n    @Bean\n    public AccountService accountService(CoinbaseExchange exchange) {\n        return new AccountService(exchange);\n    }\n} \n```\n\n```java\n@Component\npublic class MyApplication {\n\n    private AccountService accountService;\n\n    @Autowired\n    public MyApplication(AccountService accountService) {\n        this.accountService = accountService;\n    }\n\n    public void printMyAccounts() {\n        List<Account> accounts = accountService.getAccounts();\n        for(Account account: accounts) {\n            System.out.println(accounts.getBalance());\n        }\n    }\n}\n```\n\nIf you can do TDD, great. If not, add it after you've coded a solution.\n\nTests 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)."
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Ishmael Rufus\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "# Coinbase Pro\n\n[![Join the chat at https://gitter.im/irufus/gdax-java](https://badges.gitter.im/irufus/gdax-java.svg)](https://gitter.im/irufus/gdax-java?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)\n\nJava based wrapper for the [Coinbase Pro API](https://docs.pro.coinbase.com/)\n\n## Notes:\n\n> Coinbase Pro primary data sources and servers run in the Amazon US East data center. To minimize latency for API access, we recommend making requests from servers located near the US East data center.\n> Some of the methods do not yet have tests and so may not work as expected until someone tries them and fixes them at a later date. Please raise an issue in github if you want something in particular as a priority.\n> This codebase is maintained independently of Coinbase. We are not in any way affiliated with coinbase or coinbase pro.\n\n## Functions supported:\n- [x] Authentication (GET, POST, DELETE supported)\n- [x] Get Account\n- [x] Get Accounts\n- [x] Get Account History\n- [x] Get Holds\n- [x] Place a new Order (limit order)\n- [x] Get an Order\n- [x] Cancel an Order\n- [x] List all open Orders\n- [x] Get Market Data\n- [x] List fills\n- [x] List Products\n- [x] HTTP Error code support\n- [x] List of Currencies - from Accounts\n- [x] Withdrawals - from coinbase accounts / payment methods / crypto account address\n- [x] Deposits - from coinbase accounts / payment methods\n- [x] Transfers - from coinbase accounts\n- [x] Payment methods - coinbase / payment methods\n- [x] Reports\n- [x] Pagination support for all calls that support it.\n- [x] Pagination support for all calls that support it.\n- [x] Sandbox support - *sandbox support was dropped by gdax so this is now redundant*\n    \n### In Development\n\nCheck the issues on the repo for open items to work on.\nPlease join the gitter channel if you have any questions. Support always welcome. \nNote the channel uses the legacy name of 'gdax-java' rather than 'coinbase-pro-java'\n\n### Contributing\n\nPlease see CONTRIBUTE.md if your interested in getting involved.\n\n## Usage\n--------\n\n**If you commit your secure keys, passphrase or secrete, disable/delete them from Coinbase Pro immediately**.\n\n1. tests can be run with `./gradlew test`, and `./gradlew integrationTest`.\n    1. unit tests have file/class names ending `Test` and run locally\n    1. integration tests have file/class names ending `IntegrationTest` and should run against the sandbox api \n\n## Examples\n\nTo make use of this library you only need a reference to the Service that you want. \n - For Accounts, get an instance of the `AccountService` See CONTRIBUTING.md for an example of how to do this \n - For MarketData, use the `MarketDataService`, and so on.\n\n## Examples\n--------\n\nAt present the Services and Data objects returned should match the interface specified in the Coinbase Pro api here: [https://docs.pro.coinbase.com/#api](https://docs.pro.coinbase.com/#api)\n\nEach `Service` class requires the `CoinbaseExchange` object (see CONTRIBUTING.md for examples of how to create this) so that methods calling the REST endpoints can be made, using a `RestTemplate` that has\nthe correct headers and signatures is used.\n\n## API\n--------\n\nThis library is as set up as follows:\n(Note: this section is likely to change over time)\n\n- `AccountService.getAccounts()` - returns a List Accounts\n- `AccountService.getAccountHistory(String accountId)` - returns the history for a given account as a List\n- `AccountService.getHolds(String accountId)` - returns a List of all held funds for a given account.\n- `DepositService.depositViaPaymentMethod(BigDecimal amount, String currency, String paymentMethodId)` - makes a deposit from a stored payment method into your GDAX account\n- `DepositService.coinbaseDeposit(BigDecimal amount, String currency, String coinbaseAccountId)` - makes a deposit from a coinbase account into your GDAX account\n- `MarketDataService.getMarketDataOrderBook(String productId, String level)` - a call to ProductService.getProducts() will return the order book for a given product. You can then use the WebsocketFeed api to keep your orderbook up to date. This is implemented in this codebase. Level can be 1 (top bid/ask only), 2 (top 50 bids/asks only), 3 (entire order book - takes a while to pull the data.)\n- `OrderService.getOpenOrders(String accountId)` - returns a List of Orders for any outstanding orders\n- `OrderService.cancelOrder(String orderId)` - cancels a given order\n- `OrderService.createOrder(NewOrderSingle aSingleOrder)` - construct an order and send it to this method to place an order for a given product on the exchange.\n- `PaymentService.getCoinbaseAccounts()` - gets the coinbase accounts for the logged in user\n- `PaymentService.getPaymentTypes()` - gets the payment types available for the logged in user\n- `ProductService.getProducts()` - returns a List of Products available from the exchange - BTC-USD, BTC-EUR, BTC-GBP, etc.\n- `ReportService.createReport(String product, String startDate, String endDate)` - not certain about this one as its untested but presumably it generates a report of a given product's trade history for the dates supplied - dates are assumed to be ISO 8601 compliant\n- `TransferService.transfer(String type, BigDecimal amount, String coinbaseAccountId)` - initiates a transfer to your (standard) Coinbase account. \n- `UserAccountService.getTrailingVolume()` - Returns the 30 day trailing volume information from all accounts\n- `WithdrawalsService` - methods that enable Withdrawals from a Coinbase-Pro account to a Coinbase Account/Payment method\n\n\n## WebsocketFeed API\n---------------------\n\nThe WebsocketFeed is implemented and works. However, there are techniques to using it successfully for production use - e.g. monitoring for 'heartbeats'. \n\nTo use the WSF check out the API documentation and look at usages of `websocketFeed.subscribe(String)` as an example that already works.\n\n## Updates - v 0.11.0\n-------------------\n- decoupling the `api` code from the spring boot desktop client application so the api can be used and eventually published as a library.\n- decoupling `model` code so that it can become shared/common for multiple projects and make building out a FIX client potentially easier\n- decoupling the `websocketfeed` code from the api implementation\n- new `security` module so websocketfeed and api can share the `Signature` code\n- removal of the `gui` desktop app - this needs rebuilding properly (with tests)\n- new modularised multi-project gradle build\n- centralised dependency versioning for libraries, in the root `build.gradle` as its easier to manage them in a single location\n- segregating the unit tests from the integration tests\n- updated api/sandbox-api endpoints for use with tests\n- renaming project to `Coinbase-Pro-java` in the `settings.gradle` file\n- remove joda time lib in favour of the standard library Instant implementation `JavaTimeModule` for the `ObjectMapper`\n- updated libraries to newer versions: spring boot, jackson, gson, etc.\n- removal of Gson in favour of Jackson libs\n- updated classes to use constructor injection rather than field based \n\n\n## Updates - v 0.9.1\n-------------------\n- building an order book that works ready for a desktop client.\n\n## Updates\n---------\n- converted to using Gradle as a build tool\n- converted to using SpringBoot for DI and request building\n- updated all libraries used - removed some unnecessary libraries\n- refactored the code to remove error handling from every method (rightly/wrongly) - its easier to maintain and extend now as a result\n- more modular code that matches the service api - favour composition over inheritance\n- removed a lot of boilerplate code\n- logging added - Logging will output an equivalent curl command now for each get/post/delete request so that when debugging you can copy the curl request and execute it on the command line.\n- service tests added for sanity - no unit tests against the data objects\n- better configuration options using `application.yml` for your live environment and `application-test.yml` for your sandbox environment.\n- banner displayed (specific to each environment) :)\n- generally more structure.\n- added pagination to all the relevant calls (some not supported since it seems pointless due to the limited offering from gdax - e.g. products)\n- GDAX is updating its API without updating documentation - I've fixed an issue with market data because of this.\n- WebsocketFeed added\n- OrderBook GUI component added - enable in the `application.yml` by setting enabled to `true`\n- LiveOrderBook (full channel) Implemented and viewable via the GUI when enabled\n\n## TODO\n-------\n- add pagination versions of all endpoints, or offer a way to append to the endpoint urls.\n\nFrom the GDAX API documentation the Websocket implementation follows the following implementation:\n  Send a subscribe message for the product(s) of interest and the full channel.\n  Queue any messages received over the websocket stream.\n  Make a REST request for the order book snapshot from the REST feed.\n  Playback queued messages, discarding sequence numbers before or equal to the snapshot sequence number.\n  Apply playback messages to the snapshot as needed (see below).\n  After playback is complete, apply real-time stream messages in sequential order, queuing any that arrive out of order for later processing.\n  Discard messages once they've been processed.\n\n"
  },
  {
    "path": "api/build.gradle",
    "content": "test {\n    useJUnitPlatform()\n}\n\ndependencies {\n    compile project(':model')\n    compile project(':security')\n    implementation 'com.fasterxml.jackson.core:jackson-core'\n    implementation 'com.fasterxml.jackson.core:jackson-databind'\n    implementation 'com.fasterxml.jackson.core:jackson-annotations'\n    implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8'\n    implementation 'org.springframework.boot:spring-boot-starter-web'\n    implementation 'org.springframework:spring-context'\n    implementation 'org.slf4j:slf4j-api'\n    implementation 'ch.qos.logback:logback-classic'\n    implementation 'ch.qos.logback:logback-core'\n\n    testImplementation 'junit:junit'\n    testImplementation 'org.junit.jupiter:junit-jupiter-api'\n    testImplementation 'org.junit.jupiter:junit-jupiter-params'\n    testImplementation 'org.junit.jupiter:junit-jupiter-engine'\n    testImplementation 'org.junit.platform:junit-platform-suite-api'\n    testImplementation 'org.springframework.boot:spring-boot-test'\n    testImplementation 'org.springframework.boot:spring-boot-starter-test'\n}"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/accounts/Account.java",
    "content": "package com.coinbase.exchange.api.accounts;\n\nimport java.math.BigDecimal;\n\n/**\n * Created by irufus on 2/18/15.\n */\npublic class Account {\n    private String id;\n    private String currency;\n    private BigDecimal balance;\n    private BigDecimal available;\n    private BigDecimal hold;\n    private String profile_id;\n\n    public Account() {\n        this.balance = BigDecimal.ZERO;\n        this.available = BigDecimal.ZERO;\n        this.hold = BigDecimal.ZERO;\n    }\n\n    public Account(String id,\n                   String currency,\n                   BigDecimal balance,\n                   BigDecimal available,\n                   BigDecimal hold,\n                   String profile_id) {\n        this.id = id;\n        this.currency = currency;\n        this.balance = balance;\n        this.available = available;\n        this.hold = hold;\n        this.profile_id = profile_id;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public BigDecimal getBalance() {\n        return balance;\n    }\n\n    public void setBalance(BigDecimal balance) {\n        this.balance = balance;\n    }\n\n    public BigDecimal getHold() {\n        return hold;\n    }\n\n    public void setHold(BigDecimal hold) {\n        this.hold = hold;\n    }\n\n    public BigDecimal getAvailable() {\n        return available;\n    }\n\n    public void setAvailable(BigDecimal available) {\n        this.available = available;\n    }\n\n    public String getCurrency() {\n        return currency;\n    }\n\n    public void setCurrency(String currency) {\n        this.currency = currency;\n    }\n\n    public String getProfile_id() {\n        return profile_id;\n    }\n\n    public void setProfile_id(String profile_id) {\n        this.profile_id = profile_id;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/accounts/AccountHistory.java",
    "content": "package com.coinbase.exchange.api.accounts;\n\nimport com.coinbase.exchange.model.Detail;\n\nimport java.math.BigDecimal;\n\n/**\n * Created by irufus on 2/18/15.\n */\npublic class AccountHistory {\n    private Integer id;\n    private String created_at;\n    private BigDecimal amount;\n    private BigDecimal balance;\n    private String type;\n    private Detail detail;\n\n    public AccountHistory() {}\n\n    public Integer getId() {\n        return id;\n    }\n\n    public void setId(Integer id) {\n        this.id = id;\n    }\n\n    public String getCreated_at() {\n        return created_at;\n    }\n\n    public void setCreated_at(String created_at) {\n        this.created_at = created_at;\n    }\n\n    public BigDecimal getAmount() {\n        return amount;\n    }\n\n    public void setAmount(BigDecimal amount) {\n        this.amount = amount;\n    }\n\n    public BigDecimal getBalance() {\n        return balance;\n    }\n\n    public void setBalance(BigDecimal balance) {\n        this.balance = balance;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public Detail getDetail() {\n        return detail;\n    }\n\n    public void setDetail(Detail detail) {\n        this.detail = detail;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/accounts/AccountService.java",
    "content": "package com.coinbase.exchange.api.accounts;\n\nimport com.coinbase.exchange.model.Hold;\nimport com.coinbase.exchange.api.exchange.CoinbaseExchange;\nimport org.springframework.core.ParameterizedTypeReference;\n\nimport java.util.List;\n\n/**\n * Created by robevansuk on 25/01/2017.\n */\npublic class AccountService {\n\n    final CoinbaseExchange exchange;\n\n    public AccountService(final CoinbaseExchange exchange) {\n        this.exchange = exchange;\n    }\n\n    public static final String ACCOUNTS_ENDPOINT = \"/accounts\";\n\n    public List<Account> getAccounts(){\n        return exchange.getAsList(ACCOUNTS_ENDPOINT, new ParameterizedTypeReference<Account[]>(){});\n    }\n\n    public Account getAccount(String id) {\n        return exchange.get(ACCOUNTS_ENDPOINT + \"/\" + id, new ParameterizedTypeReference<Account>() {});\n    }\n\n    public List<AccountHistory> getAccountHistory(String accountId) {\n        String accountHistoryEndpoint = ACCOUNTS_ENDPOINT + \"/\" + accountId + \"/ledger\";\n        return exchange.getAsList(accountHistoryEndpoint, new ParameterizedTypeReference<AccountHistory[]>(){});\n    }\n\n    public List<AccountHistory> getPagedAccountHistory(String accountId,\n                                                       String beforeOrAfter,\n                                                       Integer pageNumber,\n                                                       Integer limit) {\n\n        String accountHistoryEndpoint = ACCOUNTS_ENDPOINT + \"/\" + accountId + \"/ledger\";\n        return exchange.pagedGetAsList(accountHistoryEndpoint,\n                new ParameterizedTypeReference<AccountHistory[]>(){},\n                beforeOrAfter,\n                pageNumber,\n                limit);\n    }\n\n    public List<Hold> getHolds(String accountId) {\n        String holdsEndpoint = ACCOUNTS_ENDPOINT + \"/\" + accountId + \"/holds\";\n        return exchange.getAsList(holdsEndpoint, new ParameterizedTypeReference<Hold[]>(){});\n    }\n\n    public List<Hold> getPagedHolds(String accountId,\n                                    String beforeOrAfter,\n                                    Integer pageNumber,\n                                    Integer limit) {\n        String holdsEndpoint = ACCOUNTS_ENDPOINT + \"/\" + accountId + \"/holds\";\n        return exchange.pagedGetAsList(holdsEndpoint,\n                new ParameterizedTypeReference<Hold[]>(){},\n                beforeOrAfter,\n                pageNumber,\n                limit);\n    }\n\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/deposits/DepositService.java",
    "content": "package com.coinbase.exchange.api.deposits;\n\nimport com.coinbase.exchange.model.CoinbasePaymentRequest;\nimport com.coinbase.exchange.model.PaymentResponse;\nimport com.coinbase.exchange.api.exchange.CoinbaseExchange;\nimport org.springframework.core.ParameterizedTypeReference;\n\nimport java.math.BigDecimal;\n\n/**\n * Created by robevansuk on 16/02/2017.\n */\npublic class DepositService {\n\n    private static final String DEPOSIT_ENDPOINT = \"/deposits\";\n    private static final String PAYMENTS = \"/payment-method\";\n    private static final String COINBASE_PAYMENT = \"/coinbase-account\";\n\n    final CoinbaseExchange exchange;\n\n    public DepositService(final CoinbaseExchange exchange) {\n        this.exchange = exchange;\n    }\n\n    /**\n     * @param amount\n     * @param currency\n     * @param paymentMethodId\n     * @return PaymentResponse\n     */\n    public PaymentResponse depositViaPaymentMethod(BigDecimal amount, String currency, final String paymentMethodId) {\n        CoinbasePaymentRequest coinbasePaymentRequest = new CoinbasePaymentRequest(amount, currency, paymentMethodId);\n        return exchange.post(DEPOSIT_ENDPOINT + PAYMENTS,\n                new ParameterizedTypeReference<PaymentResponse>(){},\n                coinbasePaymentRequest);\n    }\n\n    /**\n     * @param amount\n     * @param currency\n     * @param coinbaseAccountId\n     * @return PaymentResponse\n     */\n    public PaymentResponse depositViaCoinbase(BigDecimal amount, String currency, String coinbaseAccountId) {\n        CoinbasePaymentRequest coinbasePaymentRequest = new CoinbasePaymentRequest(amount, currency, coinbaseAccountId);\n        return exchange.post(DEPOSIT_ENDPOINT + COINBASE_PAYMENT,\n                new ParameterizedTypeReference<PaymentResponse>(){},\n                coinbasePaymentRequest);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/exchange/CoinbaseExchange.java",
    "content": "package com.coinbase.exchange.api.exchange;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.http.HttpEntity;\n\nimport java.util.List;\n\npublic interface CoinbaseExchange {\n\n    String getBaseUrl();\n    <R> HttpEntity<String> securityHeaders(String endpoint, String method, String body);\n    <T> T get(String endpoint, ParameterizedTypeReference<T> type);\n    <T> T pagedGet(String endpoint, ParameterizedTypeReference<T> responseType, String beforeOrAfter, Integer pageNumber, Integer limit);\n    <T> List<T> getAsList(String endpoint, ParameterizedTypeReference<T[]> type);\n    <T> List<T> pagedGetAsList(String endpoint, ParameterizedTypeReference<T[]> responseType, String beforeOrAfter, Integer pageNumber, Integer limit);\n    <T, R> T post(String endpoint, ParameterizedTypeReference<T> type, R jsonObject);\n    <T> T delete(String endpoint, ParameterizedTypeReference<T> type);\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/exchange/CoinbaseExchangeImpl.java",
    "content": "package com.coinbase.exchange.api.exchange;\n\nimport com.coinbase.exchange.security.Signature;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.http.HttpEntity;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.client.HttpClientErrorException;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static java.util.Collections.emptyList;\n\n/**\n * This class acts as a central point for providing user configuration and making GET/POST/PUT requests as well as\n * getting responses as Lists of objects rather than arrays.\n */\npublic class CoinbaseExchangeImpl implements CoinbaseExchange {\n\n    static final Logger log = LoggerFactory.getLogger(CoinbaseExchangeImpl.class.getName());\n\n    private final String publicKey;\n    private final String passphrase;\n    private final String baseUrl;\n    private final Signature signature;\n    private final ObjectMapper objectMapper;\n    private final RestTemplate restTemplate;\n\n    public CoinbaseExchangeImpl(final String publicKey,\n                                final String passphrase,\n                                final String baseUrl,\n                                final Signature signature,\n                                final ObjectMapper objectMapper) {\n        this.publicKey = publicKey;\n        this.passphrase = passphrase;\n        this.baseUrl = baseUrl;\n        this.signature = signature;\n        this.objectMapper = objectMapper;\n        this.restTemplate = new RestTemplate();\n    }\n\n    @Override\n    public <T> T get(String resourcePath, ParameterizedTypeReference<T> responseType) {\n        try {\n            ResponseEntity<T> responseEntity = restTemplate.exchange(getBaseUrl() + resourcePath,\n                    HttpMethod.GET,\n                    securityHeaders(resourcePath,\n                    \"GET\",\n                     \"\"),\n                    responseType);\n            return responseEntity.getBody();\n        } catch (HttpClientErrorException ex) {\n            log.error(\"GET request Failed for '\" + resourcePath + \"': \" + ex.getResponseBodyAsString());\n        }\n        return null;\n    }\n\n    @Override\n    public <T> List<T> getAsList(String resourcePath, ParameterizedTypeReference<T[]> responseType) {\n       T[] result = get(resourcePath, responseType);\n\n       return result == null ? emptyList() : Arrays.asList(result);\n    }\n\n    @Override\n    public <T> T pagedGet(String resourcePath,\n                          ParameterizedTypeReference<T> responseType,\n                          String beforeOrAfter,\n                          Integer pageNumber,\n                          Integer limit) {\n        resourcePath += \"?\" + beforeOrAfter + \"=\" + pageNumber + \"&limit=\" + limit;\n        return get(resourcePath, responseType);\n    }\n\n    @Override\n    public <T> List<T> pagedGetAsList(String resourcePath,\n                          ParameterizedTypeReference<T[]> responseType,\n                          String beforeOrAfter,\n                          Integer pageNumber,\n                          Integer limit) {\n        T[] result = pagedGet(resourcePath, responseType, beforeOrAfter, pageNumber, limit );\n        return result == null ? emptyList() : Arrays.asList(result);\n    }\n\n    @Override\n    public <T> T delete(String resourcePath, ParameterizedTypeReference<T> responseType) {\n        try {\n            ResponseEntity<T> response = restTemplate.exchange(getBaseUrl() + resourcePath,\n                HttpMethod.DELETE,\n                securityHeaders(resourcePath, \"DELETE\", \"\"),\n                responseType);\n            return response.getBody();\n        } catch (HttpClientErrorException ex) {\n            log.error(\"DELETE request Failed for '\" + resourcePath + \"': \" + ex.getResponseBodyAsString());\n        }\n        return null;\n    }\n\n    @Override\n    public <T, R> T post(String resourcePath,  ParameterizedTypeReference<T> responseType, R jsonObj) {\n        String jsonBody = toJson(jsonObj);\n\n        try {\n            ResponseEntity<T> response = restTemplate.exchange(getBaseUrl() + resourcePath,\n                    HttpMethod.POST,\n                    securityHeaders(resourcePath, \"POST\", jsonBody),\n                    responseType);\n            return response.getBody();\n        } catch (HttpClientErrorException ex) {\n            log.error(\"POST request Failed for '\" + resourcePath + \"': \" + ex.getResponseBodyAsString());\n        }\n        return null;\n    }\n\n    @Override\n    public String getBaseUrl() {\n        return baseUrl;\n    }\n\n    @Override\n    public HttpEntity<String> securityHeaders(String endpoint, String method, String jsonBody) {\n        HttpHeaders headers = new HttpHeaders();\n\n        String timestamp = Instant.now().getEpochSecond() + \"\";\n        String resource = endpoint.replace(getBaseUrl(), \"\");\n\n        headers.add(\"accept\", \"application/json\");\n        headers.add(\"content-type\", \"application/json\");\n        headers.add(\"User-Agent\", \"gdax-java unofficial coinbase pro api library\");\n        headers.add(\"CB-ACCESS-KEY\", publicKey);\n        headers.add(\"CB-ACCESS-SIGN\", signature.generate(resource, method, jsonBody, timestamp));\n        headers.add(\"CB-ACCESS-TIMESTAMP\", timestamp);\n        headers.add(\"CB-ACCESS-PASSPHRASE\", passphrase);\n\n        curlRequest(method, jsonBody, headers, resource);\n\n        return new HttpEntity<>(jsonBody, headers);\n    }\n\n    /**\n     * Purely here for logging an equivalent curl request for debugging\n     * note that the signature is time-sensitive and has a time to live of about 1 minute after which the request\n     * is no longer valid.\n     */\n    private void curlRequest(String method, String jsonBody, HttpHeaders headers, String resource) {\n        String curlTest = \"curl \";\n        for (String key : headers.keySet()){\n            curlTest +=  \"-H '\" + key + \":\" + headers.get(key).get(0) + \"' \";\n        }\n        if (jsonBody!=null && !jsonBody.equals(\"\"))\n            curlTest += \"-d '\" + jsonBody + \"' \";\n\n        curlTest += \"-X \" + method + \" \" + getBaseUrl() + resource;\n        log.debug(curlTest);\n    }\n\n    private String toJson(Object object) {\n        try {\n            return objectMapper.writeValueAsString(object);\n        } catch (JsonProcessingException e) {\n            log.error(\"Unable to serialize\", e);\n            throw new RuntimeException(\"Unable to serialize\");\n        }\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/marketdata/MarketData.java",
    "content": "package com.coinbase.exchange.api.marketdata;\n\nimport java.util.List;\n\n/**\n * Created by robevansuk on 09/02/2017.\n */\npublic class MarketData {\n\n    private Long sequence;\n    private List<OrderItem> bids; // price, size, orders\n    private List<OrderItem> asks; // price, size, orders\n\n    public MarketData() { }\n\n    public MarketData(Long sequence, List<OrderItem> bids, List<OrderItem> asks) {\n        this.sequence = sequence;\n        this.bids = bids;\n        this.asks = asks;\n    }\n\n    public Long getSequence() {\n        return sequence;\n    }\n\n    public void setSequence(Long sequence) {\n        this.sequence = sequence;\n    }\n\n    public List<OrderItem> getBids() {\n        return bids;\n    }\n\n    public void setBids(List<OrderItem> bids) {\n        this.bids = bids;\n    }\n\n    public List<OrderItem> getAsks() {\n        return asks;\n    }\n\n    public void setAsks(List<OrderItem> asks) {\n        this.asks = asks;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/marketdata/MarketDataService.java",
    "content": "package com.coinbase.exchange.api.marketdata;\n\nimport com.coinbase.exchange.api.exchange.CoinbaseExchange;\nimport org.springframework.core.ParameterizedTypeReference;\n\nimport java.util.List;\n\n/**\n * Created by robevansuk on 07/02/2017.\n */\npublic class MarketDataService {\n\n    final CoinbaseExchange exchange;\n\n    public MarketDataService(final CoinbaseExchange exchange) {\n        this.exchange = exchange;\n    }\n\n    public static final String PRODUCT_ENDPOINT = \"/products\";\n\n    public MarketData getMarketDataOrderBook(String productId, int level) {\n        String marketDataEndpoint = PRODUCT_ENDPOINT + \"/\" + productId + \"/book\";\n        if(level != 1)\n            marketDataEndpoint += \"?level=\" + level;\n       return exchange.get(marketDataEndpoint, new ParameterizedTypeReference<MarketData>(){});\n    }\n\n    public List<Trade> getTrades(String productId) {\n        String tradesEndpoint = PRODUCT_ENDPOINT + \"/\" + productId + \"/trades\";\n        return exchange.getAsList(tradesEndpoint, new ParameterizedTypeReference<Trade[]>(){});\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/marketdata/Message.java",
    "content": "package com.coinbase.exchange.api.marketdata;\n\nimport java.math.BigDecimal;\n\n/**\n * Created by irufus on 2/28/15.\n */\npublic class Message {\n    private String type;\n    private Long sequence;\n    private String order_id;\n    private BigDecimal size;\n    private BigDecimal price;\n    private String side;\n    private BigDecimal remaining_size;\n    private String reason;\n    private String maker_order_id;\n    private String taker_order_id;\n    private String time;\n\n    public BigDecimal getRemaining_size() {\n        return remaining_size;\n    }\n\n    public void setRemaining_size(BigDecimal remaining_size) {\n        this.remaining_size = remaining_size;\n    }\n\n    public String getSide() {\n        return side;\n    }\n\n    public void setSide(String side) {\n        this.side = side;\n    }\n\n    public BigDecimal getPrice() {\n        return price;\n    }\n\n    public void setPrice(BigDecimal price) {\n        this.price = price;\n    }\n\n    public BigDecimal getSize() {\n        return size;\n    }\n\n    public void setSize(BigDecimal size) {\n        this.size = size;\n    }\n\n    public String getOrder_id() {\n        return order_id;\n    }\n\n    public void setOrder_id(String order_id) {\n        this.order_id = order_id;\n    }\n\n    public Long getSequence() {\n        return sequence;\n    }\n\n    public void setSequence(Long sequence) {\n        this.sequence = sequence;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public String getReason() {\n        return reason;\n    }\n\n    public void setReason(String reason) {\n        this.reason = reason;\n    }\n\n    public String getMaker_order_id() {\n        return maker_order_id;\n    }\n\n    public void setMaker_order_id(String maker_order_id) {\n        this.maker_order_id = maker_order_id;\n    }\n\n    public String getTaker_order_id() {\n        return taker_order_id;\n    }\n\n    public void setTaker_order_id(String taker_order_id) {\n        this.taker_order_id = taker_order_id;\n    }\n\n    public String getTime() {\n        return time;\n    }\n\n    public void setTime(String time) {\n        this.time = time;\n    }\n\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/marketdata/MessageEX.java",
    "content": "package com.coinbase.exchange.api.marketdata;\n\n/**\n * Created by irufus on 3/2/15.\n */\npublic class MessageEX {\n    public static class MessageType{\n        public final static String RECEIEVED = \"received\";\n        public final static String OPEN = \"open\";\n        public final static String DONE = \"done\";\n        public final static String MATCH = \"match\";\n        public final static String CHANGE = \"change\";\n        public final static String ERROR = \"error\";\n    }\n    public static class MessageSide{\n        public final static String BUY = \"buy\";\n        public final static String SELL = \"sell\";\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/marketdata/OrderItem.java",
    "content": "package com.coinbase.exchange.api.marketdata;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport org.springframework.util.CollectionUtils;\n\nimport java.math.BigDecimal;\nimport java.util.List;\n\n/**\n * Created by robevansuk on 20/03/2017.\n */\npublic class OrderItem implements Comparable {\n\n    private final BigDecimal price;\n    private final BigDecimal size;\n    private final String orderId;\n    private final BigDecimal num;\n\n    @JsonCreator\n    public OrderItem(List<String> limitOrders) {\n        if (CollectionUtils.isEmpty(limitOrders) || limitOrders.size() < 3) {\n            throw new IllegalArgumentException(\"LimitOrders was empty - check connection to the exchange\");\n        }\n        price =  new BigDecimal(limitOrders.get(0));\n        size = new BigDecimal(limitOrders.get(1));\n        if (isString(limitOrders.get(2))) {\n            orderId = limitOrders.get(2);\n            num = new BigDecimal(1);\n        } else {\n            orderId = null;\n            num = new BigDecimal(1);\n        }\n    }\n\n    public BigDecimal getPrice() {\n        return price;\n    }\n\n    public BigDecimal getSize() {\n        return size;\n    }\n\n    public String getOrderId() {\n        return orderId;\n    }\n\n    public BigDecimal getNum() {\n        return num;\n    }\n\n    @Override\n    public int compareTo(Object o) {\n        return this.getPrice().compareTo(((OrderItem)o).getPrice()) * -1;\n    }\n\n    public boolean isString(String value) {\n        boolean isDecimalSeparatorFound = false;\n\n        for (char c : value.substring( 1 ).toCharArray()) {\n            if (!Character.isDigit( c ) ) {\n                if (c == '.' && !isDecimalSeparatorFound) {\n                    isDecimalSeparatorFound = true;\n                    continue;\n                }\n                return false;\n            }\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/marketdata/Trade.java",
    "content": "package com.coinbase.exchange.api.marketdata;\n\nimport java.math.BigDecimal;\nimport java.time.Instant;\n\n/**\n * Created by robevansuk on 12/03/2017.\n */\npublic class Trade {\n    Instant time;\n    Long trade_id;\n    BigDecimal price;\n    BigDecimal size;\n    String side;\n\n    public Trade() {}\n\n    public Trade(Instant time, Long trade_id, BigDecimal price, BigDecimal size, String side) {\n        this.time = time;\n        this.trade_id = trade_id;\n        this.price = price;\n        this.size = size;\n        this.side = side;\n    }\n\n    public Instant getTime() {\n        return time;\n    }\n\n    public void setTime(Instant time) {\n        this.time = time;\n    }\n\n    public Long getTrade_id() {\n        return trade_id;\n    }\n\n    public void setTrade_id(Long trade_id) {\n        this.trade_id = trade_id;\n    }\n\n    public BigDecimal getPrice() {\n        return price;\n    }\n\n    public void setPrice(BigDecimal price) {\n        this.price = price;\n    }\n\n    public BigDecimal getSize() {\n        return size;\n    }\n\n    public void setSize(BigDecimal size) {\n        this.size = size;\n    }\n\n    public String getSide() {\n        return side;\n    }\n\n    public void setSide(String side) {\n        this.side = side;\n    }\n\n    public String toString(){\n        return side.toUpperCase() + \" \" + size +\"@\" + price;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/orders/Order.java",
    "content": "package com.coinbase.exchange.api.orders;\n\n/**\n * Created by robevansuk on 03/02/2017.\n */\npublic class Order {\n    String id;\n    String size;\n    String price;\n    String product_id;\n    String side;\n    String stp;\n    String type;\n    String time_in_force;\n    String post_only;\n    String created_at;\n    String fill_fees;\n    String filled_size;\n    String executed_value;\n    String status;\n\n    public Order() {}\n\n    public Order(String id, String price, String size, String product_id, String side, String stp, String type,\n                 String time_in_force, String post_only, String created_at, String fill_fees, String filled_size,\n                 String executed_value, String status, Boolean settled) {\n        this.id = id;\n        this.price = price;\n        this.size = size;\n        this.product_id = product_id;\n        this.side = side;\n        this.stp = stp;\n        this.type = type;\n        this.time_in_force = time_in_force;\n        this.post_only = post_only;\n        this.created_at = created_at;\n        this.fill_fees = fill_fees;\n        this.filled_size = filled_size;\n        this.executed_value = executed_value;\n        this.status = status;\n        this.settled = settled;\n    }\n\n    Boolean settled;\n\n    public Order(OrderBuilder builder) {\n        this.id = builder.getId();\n        this.size = builder.getSize();\n        this.price = builder.getPrice();\n        this.product_id = builder.getProduct_id();\n        this.status = builder.getStatus();\n        this.filled_size = builder.getFilled_size();\n        this.fill_fees = builder.getFill_fees();\n        this.settled = builder.getSettled();\n        this.side = builder.getSide();\n        this.created_at = builder.getCreated_at();\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getPrice() {\n        return price;\n    }\n\n    public void setPrice(String price) {\n        this.price = price;\n    }\n\n    public String getSize() {\n        return size;\n    }\n\n    public void setSize(String size) {\n        this.size = size;\n    }\n\n    public String getProduct_id() {\n        return product_id;\n    }\n\n    public void setProduct_id(String product_id) {\n        this.product_id = product_id;\n    }\n\n    public String getSide() {\n        return side;\n    }\n\n    public void setSide(String side) {\n        this.side = side;\n    }\n\n    public String getStp() {\n        return stp;\n    }\n\n    public void setStp(String stp) {\n        this.stp = stp;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public String getTime_in_force() {\n        return time_in_force;\n    }\n\n    public void setTime_in_force(String time_in_force) {\n        this.time_in_force = time_in_force;\n    }\n\n    public String getPost_only() {\n        return post_only;\n    }\n\n    public void setPost_only(String post_only) {\n        this.post_only = post_only;\n    }\n\n    public String getCreated_at() {\n        return created_at;\n    }\n\n    public void setCreated_at(String created_at) {\n        this.created_at = created_at;\n    }\n\n    public String getFill_fees() {\n        return fill_fees;\n    }\n\n    public void setFill_fees(String fill_fees) {\n        this.fill_fees = fill_fees;\n    }\n\n    public String getFilled_size() {\n        return filled_size;\n    }\n\n    public void setFilled_size(String filled_size) {\n        this.filled_size = filled_size;\n    }\n\n    public String getExecuted_value() {\n        return executed_value;\n    }\n\n    public void setExecuted_value(String executed_value) {\n        this.executed_value = executed_value;\n    }\n\n    public String getStatus() {\n        return status;\n    }\n\n    public void setStatus(String status) {\n        this.status = status;\n    }\n\n    public Boolean getSettled() {\n        return settled;\n    }\n\n    public void setSettled(Boolean settled) {\n        this.settled = settled;\n    }\n\n    public String toString() {\n        String orderString = getSide();\n        orderString += \": \" + getProduct_id();\n        orderString += \": \" + getPrice();\n        orderString += \": \" + getSize();\n        return orderString;\n    }\n\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/orders/OrderBuilder.java",
    "content": "package com.coinbase.exchange.api.orders;\n\n/**\n * Created by irufus on 2/18/15.\n */\npublic class OrderBuilder {\n\n    private String id;\n    private String size;\n    private String price;\n    private String product_id;\n    private String status;\n    private String filled_size;\n    private String fill_fees;\n    private String side;\n    private String created_at;\n    private Boolean settled;\n\n    public OrderBuilder setId(String id) {\n        this.id = id;\n        return this;\n    }\n\n    public OrderBuilder setSize(String size) {\n        this.size = size;\n        return this;\n    }\n\n    public OrderBuilder setPrice(String price) {\n        this.price = price;\n        return this;\n    }\n\n    public OrderBuilder setProduct_id(String product_id) {\n        this.product_id = product_id;\n        return this;\n    }\n\n    public OrderBuilder setStatus(String status) {\n        this.status = status;\n        return this;\n    }\n\n    public OrderBuilder setFilled_size(String filled_size) {\n        this.filled_size = filled_size;\n        return this;\n    }\n\n    public OrderBuilder setFill_fees(String fill_fees) {\n        this.fill_fees = fill_fees;\n        return this;\n    }\n\n    public OrderBuilder setSettled(Boolean settled) {\n        this.settled = settled;\n        return this;\n    }\n\n    public OrderBuilder setSide(String side) {\n        this.side = side;\n        return this;\n    }\n\n    public OrderBuilder setCreated_at(String created_at) {\n        this.created_at = created_at;\n        return this;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public String getSize() {\n        return size;\n    }\n\n    public String getPrice() {\n        return price;\n    }\n\n    public String getProduct_id() {\n        return product_id;\n    }\n\n    public String getStatus() {\n        return status;\n    }\n\n    public String getFilled_size() {\n        return filled_size;\n    }\n\n    public String getFill_fees() {\n        return fill_fees;\n    }\n\n    public Boolean getSettled() {\n        return settled;\n    }\n\n    public String getSide() {\n        return side;\n    }\n\n    public String getCreated_at() {\n        return created_at;\n    }\n\n    public Order build() {\n        return new Order(this);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/orders/OrderService.java",
    "content": "package com.coinbase.exchange.api.orders;\n\nimport com.coinbase.exchange.model.Fill;\nimport com.coinbase.exchange.model.Hold;\nimport com.coinbase.exchange.model.NewOrderSingle;\nimport com.coinbase.exchange.api.exchange.CoinbaseExchange;\nimport org.springframework.core.ParameterizedTypeReference;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * Created by robevansuk on 03/02/2017.\n */\npublic class OrderService {\n    public static final String ORDERS_ENDPOINT = \"/orders\";\n    public static final String FILLS_ENDPOINT = \"/fills\";\n\n    final CoinbaseExchange exchange;\n\n    public OrderService(final CoinbaseExchange exchange) {\n        this.exchange = exchange;\n    }\n\n    public List<Hold> getHolds(String accountId) {\n        return exchange.getAsList(ORDERS_ENDPOINT + \"/\" + accountId + \"/holds\", new ParameterizedTypeReference<Hold[]>(){});\n    }\n\n    public List<Order> getOpenOrders(String accountId) {\n        return exchange.getAsList(ORDERS_ENDPOINT + \"/\" + accountId + \"/orders\", new ParameterizedTypeReference<Order[]>(){});\n    }\n\n    public Order getOrder(String orderId) {\n        return exchange.get(ORDERS_ENDPOINT + \"/\" + orderId,new ParameterizedTypeReference<Order>(){});\n    }\n\n    public Order createOrder(NewOrderSingle order) {\n        return exchange.post(ORDERS_ENDPOINT, new ParameterizedTypeReference<Order>(){}, order);\n    }\n\n    public String cancelOrder(String orderId) {\n        String deleteEndpoint = ORDERS_ENDPOINT + \"/\" + orderId;\n        return exchange.delete(deleteEndpoint, new ParameterizedTypeReference<String>(){});\n    }\n\n    public List<Order> getOpenOrders() {\n        return exchange.getAsList(ORDERS_ENDPOINT, new ParameterizedTypeReference<Order[]>(){});\n    }\n\n    public List<Order> cancelAllOpenOrders() {\n        return Arrays.asList(exchange.delete(ORDERS_ENDPOINT, new ParameterizedTypeReference<Order[]>(){}));\n    }\n\n    public List<Fill> getFillsByProductId(String product_id, int resultLimit) {\n        return exchange.getAsList(FILLS_ENDPOINT + \"?product_id=\" + product_id + \"&limit=\" + resultLimit, new ParameterizedTypeReference<Fill[]>(){});\n    }\n    \n    public List<Fill> getFillByOrderId(String order_id, int resultLimit) {\n        return exchange.getAsList(FILLS_ENDPOINT + \"?order_id=\" + order_id + \"&limit=\" + resultLimit, new ParameterizedTypeReference<Fill[]>(){});\n    }\n}\n\n\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/payments/AccountLimit.java",
    "content": "package com.coinbase.exchange.api.payments;\n\n/**\n * Created by robevansuk on 16/02/2017.\n */\npublic class AccountLimit {\n\n    Integer period_in_days;\n    Amount total;\n    Amount remaining;\n\n    public AccountLimit() {}\n\n    public AccountLimit(Integer period_in_days, Amount total, Amount remaining) {\n        this.period_in_days = period_in_days;\n        this.total = total;\n        this.remaining = remaining;\n    }\n\n    public Integer getPeriod_in_days() {\n        return period_in_days;\n    }\n\n    public void setPeriod_in_days(Integer period_in_days) {\n        this.period_in_days = period_in_days;\n    }\n\n    public Amount getTotal() {\n        return total;\n    }\n\n    public void setTotal(Amount total) {\n        this.total = total;\n    }\n\n    public Amount getRemaining() {\n        return remaining;\n    }\n\n    public void setRemaining(Amount remaining) {\n        this.remaining = remaining;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/payments/Amount.java",
    "content": "package com.coinbase.exchange.api.payments;\n\nimport java.math.BigDecimal;\n\n/**\n * Created by robevansuk on 16/02/2017.\n */\npublic class Amount {\n\n    BigDecimal amount;\n    String currency;\n\n    public Amount() {}\n\n    public Amount(BigDecimal amount, String currency) {\n        this.amount = amount;\n        this.currency = currency;\n    }\n\n    public BigDecimal getAmount() {\n        return amount;\n    }\n\n    public void setAmount(BigDecimal amount) {\n        this.amount = amount;\n    }\n\n    public String getCurrency() {\n        return currency;\n    }\n\n    public void setCurrency(String currency) {\n        this.currency = currency;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/payments/BankCountry.java",
    "content": "package com.coinbase.exchange.api.payments;\n\n/**\n * Created by robevansuk on 16/02/2017.\n */\npublic class BankCountry {\n\n    String code;\n    String name;\n\n    public BankCountry() {}\n\n    public BankCountry(String code, String name) {\n        this.code = code;\n        this.name = name;\n    }\n\n    public String getCode() {\n        return code;\n    }\n\n    public void setCode(String code) {\n        this.code = code;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/payments/CoinbaseAccount.java",
    "content": "package com.coinbase.exchange.api.payments;\n\nimport java.math.BigDecimal;\n\n/**\n * Created by robevansuk on 16/02/2017.\n */\npublic class CoinbaseAccount {\n\n    private String id; //UUID\n    private String name;\n    private BigDecimal balance;\n    private String currency;\n    private String type;\n    private Boolean primary;\n    private Boolean active;\n    private DepositInformation wire_deposit_information;\n    private SepaDepositInformation sepa_deposit_information;\n\n    public CoinbaseAccount() {}\n\n    public CoinbaseAccount(String id,\n                           String name,\n                           BigDecimal balance,\n                           String currency,\n                           String type,\n                           Boolean primary,\n                           Boolean active,\n                           DepositInformation wire_deposit_information,\n                           SepaDepositInformation sepa_deposit_information) {\n        this.id = id;\n        this.name = name;\n        this.balance = balance;\n        this.currency = currency;\n        this.type = type;\n        this.primary = primary;\n        this.active = active;\n        this.wire_deposit_information = wire_deposit_information;\n        this.sepa_deposit_information = sepa_deposit_information;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public BigDecimal getBalance() {\n        return balance;\n    }\n\n    public void setBalance(BigDecimal balance) {\n        this.balance = balance;\n    }\n\n    public String getCurrency() {\n        return currency;\n    }\n\n    public void setCurrency(String currency) {\n        this.currency = currency;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public Boolean getPrimary() {\n        return primary;\n    }\n\n    public void setPrimary(Boolean primary) {\n        this.primary = primary;\n    }\n\n    public Boolean getActive() {\n        return active;\n    }\n\n    public void setActive(Boolean active) {\n        this.active = active;\n    }\n\n    public DepositInformation getWire_deposit_information() {\n        return wire_deposit_information;\n    }\n\n    public void setWire_deposit_information(DepositInformation wire_deposit_information) {\n        this.wire_deposit_information = wire_deposit_information;\n    }\n\n    public SepaDepositInformation getSepa_deposit_information() {\n        return sepa_deposit_information;\n    }\n\n    public void setSepa_deposit_information(SepaDepositInformation sepa_deposit_information) {\n        this.sepa_deposit_information = sepa_deposit_information;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/payments/DepositInformation.java",
    "content": "package com.coinbase.exchange.api.payments;\n\n/**\n * Created by robevansuk on 16/02/2017.\n */\npublic class DepositInformation {\n\n    String account_number;\n    String routing_number;\n    String bank_name;\n    String bank_address;\n    BankCountry bank_country;\n    String account_name;\n    String account_address;\n    String reference;\n\n    public DepositInformation() {}\n\n    public DepositInformation(String account_number,\n                              String routing_number,\n                              String bank_name,\n                              String bank_address,\n                              BankCountry bank_country,\n                              String account_name,\n                              String account_address,\n                              String reference) {\n        this.account_number = account_number;\n        this.routing_number = routing_number;\n        this.bank_name = bank_name;\n        this.bank_address = bank_address;\n        this.bank_country = bank_country;\n        this.account_name = account_name;\n        this.account_address = account_address;\n        this.reference = reference;\n    }\n\n    public String getAccount_number() {\n        return account_number;\n    }\n\n    public void setAccount_number(String account_number) {\n        this.account_number = account_number;\n    }\n\n    public String getRouting_number() {\n        return routing_number;\n    }\n\n    public void setRouting_number(String routing_number) {\n        this.routing_number = routing_number;\n    }\n\n    public String getBank_name() {\n        return bank_name;\n    }\n\n    public void setBank_name(String bank_name) {\n        this.bank_name = bank_name;\n    }\n\n    public String getBank_address() {\n        return bank_address;\n    }\n\n    public void setBank_address(String bank_address) {\n        this.bank_address = bank_address;\n    }\n\n    public BankCountry getBank_country() {\n        return bank_country;\n    }\n\n    public void setBank_country(BankCountry bank_country) {\n        this.bank_country = bank_country;\n    }\n\n    public String getAccount_name() {\n        return account_name;\n    }\n\n    public void setAccount_name(String account_name) {\n        this.account_name = account_name;\n    }\n\n    public String getAccount_address() {\n        return account_address;\n    }\n\n    public void setAccount_address(String account_address) {\n        this.account_address = account_address;\n    }\n\n    public String getReference() {\n        return reference;\n    }\n\n    public void setReference(String reference) {\n        this.reference = reference;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/payments/Limit.java",
    "content": "package com.coinbase.exchange.api.payments;\n\n/**\n * Created by robevansuk on 16/02/2017.\n */\npublic class Limit {\n\n    AccountLimit[] buy;\n    AccountLimit[] instant_buy;\n    AccountLimit[] sell;\n    AccountLimit[] deposit;\n\n    public Limit() {}\n\n    public Limit(AccountLimit[] buy, AccountLimit[] instant_buy, AccountLimit[] sell, AccountLimit[] deposit) {\n        this.buy = buy;\n        this.instant_buy = instant_buy;\n        this.sell = sell;\n        this.deposit = deposit;\n    }\n\n    public AccountLimit[] getBuy() {\n        return buy;\n    }\n\n    public void setBuy(AccountLimit[] buy) {\n        this.buy = buy;\n    }\n\n    public AccountLimit[] getInstant_buy() {\n        return instant_buy;\n    }\n\n    public void setInstant_buy(AccountLimit[] instant_buy) {\n        this.instant_buy = instant_buy;\n    }\n\n    public AccountLimit[] getSell() {\n        return sell;\n    }\n\n    public void setSell(AccountLimit[] sell) {\n        this.sell = sell;\n    }\n\n    public AccountLimit[] getDeposit() {\n        return deposit;\n    }\n\n    public void setDeposit(AccountLimit[] deposit) {\n        this.deposit = deposit;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/payments/PaymentService.java",
    "content": "package com.coinbase.exchange.api.payments;\n\nimport com.coinbase.exchange.api.exchange.CoinbaseExchange;\nimport org.springframework.core.ParameterizedTypeReference;\n\nimport java.util.List;\n\n/**\n * Created by robevansuk on 16/02/2017.\n */\npublic class PaymentService {\n\n    private static final String PAYMENT_METHODS_ENDPOINT = \"/payment-methods\";\n    private static final String COINBASE_ACCOUNTS_ENDPOINT = \"/coinbase-accounts\";\n\n    final CoinbaseExchange coinbaseExchange;\n\n    public PaymentService(final CoinbaseExchange coinbaseExchange) {\n        this.coinbaseExchange = coinbaseExchange;\n    }\n\n    public List<PaymentType> getPaymentTypes() {\n        return coinbaseExchange.getAsList(PAYMENT_METHODS_ENDPOINT, new ParameterizedTypeReference<PaymentType[]>(){});\n    }\n\n    public List<CoinbaseAccount> getCoinbaseAccounts() {\n        return coinbaseExchange.getAsList(COINBASE_ACCOUNTS_ENDPOINT, new ParameterizedTypeReference<CoinbaseAccount[]>() {});\n    }\n}"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/payments/PaymentType.java",
    "content": "package com.coinbase.exchange.api.payments;\n\n/**\n * Created by robevansuk on 16/02/2017.\n */\npublic class PaymentType {\n    private String id; // UUID\n    private String type; // ach_bank_account\n    private String name; // Bank of America - eBan... ********7134\n    private String currency; // USD\n    private Boolean primary_buy;\n    private Boolean primary_sell;\n    private Boolean allow_buy;\n    private Boolean allow_sell;\n    private Boolean allow_deposit;\n    private Boolean allow_withdraw;\n    private Limit limits;\n\n    public PaymentType() {}\n\n    public PaymentType(String id,\n                       String type,\n                       String name,\n                       String currency,\n                       Boolean primary_buy,\n                       Boolean primary_sell,\n                       Boolean allow_buy,\n                       Boolean allow_sell,\n                       Boolean allow_deposit,\n                       Boolean allow_withdraw,\n                       Limit limits) {\n        this.id = id;\n        this.type = type;\n        this.name = name;\n        this.currency = currency;\n        this.primary_buy = primary_buy;\n        this.primary_sell = primary_sell;\n        this.allow_buy = allow_buy;\n        this.allow_sell = allow_sell;\n        this.allow_deposit = allow_deposit;\n        this.allow_withdraw = allow_withdraw;\n        this.limits = limits;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getCurrency() {\n        return currency;\n    }\n\n    public void setCurrency(String currency) {\n        this.currency = currency;\n    }\n\n    public Boolean getPrimary_buy() {\n        return primary_buy;\n    }\n\n    public void setPrimary_buy(Boolean primary_buy) {\n        this.primary_buy = primary_buy;\n    }\n\n    public Boolean getPrimary_sell() {\n        return primary_sell;\n    }\n\n    public void setPrimary_sell(Boolean primary_sell) {\n        this.primary_sell = primary_sell;\n    }\n\n    public Boolean getAllow_buy() {\n        return allow_buy;\n    }\n\n    public void setAllow_buy(Boolean allow_buy) {\n        this.allow_buy = allow_buy;\n    }\n\n    public Boolean getAllow_sell() {\n        return allow_sell;\n    }\n\n    public void setAllow_sell(Boolean allow_sell) {\n        this.allow_sell = allow_sell;\n    }\n\n    public Boolean getAllow_deposit() {\n        return allow_deposit;\n    }\n\n    public void setAllow_deposit(Boolean allow_deposit) {\n        this.allow_deposit = allow_deposit;\n    }\n\n    public Boolean getAllow_withdraw() {\n        return allow_withdraw;\n    }\n\n    public void setAllow_withdraw(Boolean allow_withdraw) {\n        this.allow_withdraw = allow_withdraw;\n    }\n\n    public Limit getLimits() {\n        return limits;\n    }\n\n    public void setLimits(Limit limits) {\n        this.limits = limits;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/payments/SepaDepositInformation.java",
    "content": "package com.coinbase.exchange.api.payments;\n\n/**\n * Created by robevansuk on 16/02/2017.\n */\npublic class SepaDepositInformation {\n\n    String iban;\n    String swift;\n    String bank_name;\n    String bank_address;\n    String bank_country_name;\n    String account_name;\n    String account_address;\n    String reference;\n\n    public SepaDepositInformation() {}\n\n    public SepaDepositInformation(String iban,\n                                  String swift,\n                                  String bank_name,\n                                  String bank_address,\n                                  String bank_country_name,\n                                  String account_name,\n                                  String account_address,\n                                  String reference) {\n        this.iban = iban;\n        this.swift = swift;\n        this.bank_name = bank_name;\n        this.bank_address = bank_address;\n        this.bank_country_name = bank_country_name;\n        this.account_name = account_name;\n        this.account_address = account_address;\n        this.reference = reference;\n    }\n\n    public String getIban() {\n        return iban;\n    }\n\n    public void setIban(String iban) {\n        this.iban = iban;\n    }\n\n    public String getSwift() {\n        return swift;\n    }\n\n    public void setSwift(String swift) {\n        this.swift = swift;\n    }\n\n    public String getBank_name() {\n        return bank_name;\n    }\n\n    public void setBank_name(String bank_name) {\n        this.bank_name = bank_name;\n    }\n\n    public String getBank_address() {\n        return bank_address;\n    }\n\n    public void setBank_address(String bank_address) {\n        this.bank_address = bank_address;\n    }\n\n    public String getBank_country_name() {\n        return bank_country_name;\n    }\n\n    public void setBank_country_name(String bank_country_name) {\n        this.bank_country_name = bank_country_name;\n    }\n\n    public String getAccount_name() {\n        return account_name;\n    }\n\n    public void setAccount_name(String account_name) {\n        this.account_name = account_name;\n    }\n\n    public String getAccount_address() {\n        return account_address;\n    }\n\n    public void setAccount_address(String account_address) {\n        this.account_address = account_address;\n    }\n\n    public String getReference() {\n        return reference;\n    }\n\n    public void setReference(String reference) {\n        this.reference = reference;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/products/ProductService.java",
    "content": "package com.coinbase.exchange.api.products;\n\nimport com.coinbase.exchange.api.exchange.CoinbaseExchange;\nimport com.coinbase.exchange.model.Candles;\nimport com.coinbase.exchange.model.Granularity;\nimport com.coinbase.exchange.model.Product;\nimport org.springframework.core.ParameterizedTypeReference;\n\nimport java.time.Instant;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.DateTimeFormatterBuilder;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static java.util.stream.Collectors.joining;\n\n/**\n * Created by robevansuk on 03/02/2017.\n */\npublic class ProductService {\n\n    public static final String PRODUCTS_ENDPOINT = \"/products\";\n\n    final CoinbaseExchange exchange;\n\n    public ProductService(final CoinbaseExchange exchange) {\n        this.exchange = exchange;\n    }\n\n    // no paged products necessary\n    public List<Product> getProducts() {\n        return exchange.getAsList(PRODUCTS_ENDPOINT, new ParameterizedTypeReference<Product[]>() {\n        });\n    }\n\n    public Candles getCandles(String productId) {\n        return new Candles(exchange.get(PRODUCTS_ENDPOINT + \"/\" + productId + \"/candles\", new ParameterizedTypeReference<List<String[]>>() {\n        }));\n    }\n\n    public Candles getCandles(String productId, Map<String, String> queryParams) {\n        StringBuffer url = new StringBuffer(PRODUCTS_ENDPOINT + \"/\" + productId + \"/candles\");\n        if (queryParams != null && queryParams.size() != 0) {\n            url.append(\"?\");\n            url.append(queryParams.entrySet().stream()\n                    .map(entry -> entry.getKey() + \"=\" + entry.getValue())\n                    .collect(joining(\"&\")));\n        }\n        return new Candles(exchange.get(url.toString(), new ParameterizedTypeReference<List<String[]>>() {}));\n    }\n\n    /**\n     * If either one of the start or end fields are not provided then both fields will be ignored.\n     * If a custom time range is not declared then one ending now is selected.\n     */\n    public Candles getCandles(String productId, Instant startTime, Instant endTime, Granularity granularity) {\n\n        Map<String, String> queryParams = new HashMap<>();\n\n        if (startTime != null) {\n            queryParams.put(\"start\", startTime.toString());\n        }\n        if (endTime != null) {\n            queryParams.put(\"end\", endTime.toString());\n        }\n        if (granularity != null) {\n            queryParams.put(\"granularity\", granularity.get());\n        }\n\n        return getCandles(productId, queryParams);\n    }\n\n    /**\n     * The granularity field must be one of the following values: {60, 300, 900, 3600, 21600, 86400}\n     */\n    public Candles getCandles(String productId, Granularity granularity) {\n        return getCandles(productId, null, null, granularity);\n    }\n\n    /**\n     *  If either one of the start or end fields are not provided then both fields will be ignored.\n     */\n    public Candles getCandles(String productId, Instant start, Instant end) {\n        return getCandles(productId, start, end, null);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/reports/ReportRequest.java",
    "content": "package com.coinbase.exchange.api.reports;\n\n/**\n * Created by robevansuk on 16/02/2017.\n */\npublic class ReportRequest {\n\n    String type;\n    String start_date;\n    String end_date;\n\n    public ReportRequest() {}\n\n    public ReportRequest(String type, String start_date, String end_date) {\n        this.type = type;\n        this.start_date = start_date;\n        this.end_date = end_date;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public String getStart_date() {\n        return start_date;\n    }\n\n    public void setStart_date(String start_date) {\n        this.start_date = start_date;\n    }\n\n    public String getEnd_date() {\n        return end_date;\n    }\n\n    public void setEnd_date(String end_date) {\n        this.end_date = end_date;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/reports/ReportResponse.java",
    "content": "package com.coinbase.exchange.api.reports;\n\n/**\n * Created by robevansuk on 16/02/2017.\n */\npublic class ReportResponse {\n\n    String id;\n    String type;\n    String status;\n    String created_at;\n    String completed_at;\n    String expires_at;\n    String file_url;\n    TimePeriod params;\n\n    public ReportResponse() {}\n\n    public ReportResponse(String id,\n                         String type,\n                         String status,\n                         String created_at,\n                         String completed_at,\n                         String expires_at,\n                         String file_url,\n                         TimePeriod params) {\n        this.id = id;\n        this.type = type;\n        this.status = status;\n        this.created_at = created_at;\n        this.completed_at = completed_at;\n        this.expires_at = expires_at;\n        this.file_url = file_url;\n        this.params = params;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public String getStatus() {\n        return status;\n    }\n\n    public void setStatus(String status) {\n        this.status = status;\n    }\n\n    public String getCreated_at() {\n        return created_at;\n    }\n\n    public void setCreated_at(String created_at) {\n        this.created_at = created_at;\n    }\n\n    public String getCompleted_at() {\n        return completed_at;\n    }\n\n    public void setCompleted_at(String completed_at) {\n        this.completed_at = completed_at;\n    }\n\n    public String getExpires_at() {\n        return expires_at;\n    }\n\n    public void setExpires_at(String expires_at) {\n        this.expires_at = expires_at;\n    }\n\n    public String getFile_url() {\n        return file_url;\n    }\n\n    public void setFile_url(String file_url) {\n        this.file_url = file_url;\n    }\n\n    public TimePeriod getParams() {\n        return params;\n    }\n\n    public void setParams(TimePeriod params) {\n        this.params = params;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/reports/ReportService.java",
    "content": "package com.coinbase.exchange.api.reports;\n\nimport com.coinbase.exchange.api.exchange.CoinbaseExchange;\nimport org.springframework.core.ParameterizedTypeReference;\n\n/**\n * Created by robevansuk on 16/02/2017.\n */\npublic class ReportService {\n\n    private static final String REPORTS_ENDPOINT = \"/reports\";\n\n    final CoinbaseExchange coinbaseExchange;\n\n    public ReportService(final CoinbaseExchange coinbaseExchange) {\n        this.coinbaseExchange = coinbaseExchange;\n    }\n\n    // TODO untested\n    public ReportResponse createReport(String type, String startDate, String endDate){\n        ReportRequest reportRequest = new ReportRequest(type, startDate, endDate);\n        return coinbaseExchange.post(REPORTS_ENDPOINT, new ParameterizedTypeReference<ReportResponse>(){}, reportRequest);\n    }\n\n    // TODO untested\n    public ReportResponse getReportStatus(String id) {\n        return coinbaseExchange.get(REPORTS_ENDPOINT + \"/\" + id, new ParameterizedTypeReference<ReportResponse>(){});\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/reports/TimePeriod.java",
    "content": "package com.coinbase.exchange.api.reports;\n\n/**\n * Created by robevansuk on 16/02/2017.\n */\npublic class TimePeriod {\n\n    String start_date;\n    String end_date;\n\n    public TimePeriod() {}\n\n    public TimePeriod(String start_date, String end_date) {\n        this.start_date = start_date;\n        this.end_date = end_date;\n    }\n\n    public String getStart_date() {\n        return start_date;\n    }\n\n    public void setStart_date(String start_date) {\n        this.start_date = start_date;\n    }\n\n    public String getEnd_date() {\n        return end_date;\n    }\n\n    public void setEnd_date(String end_date) {\n        this.end_date = end_date;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/transfers/Transfer.java",
    "content": "package com.coinbase.exchange.api.transfers;\n\nimport java.math.BigDecimal;\n\n/**\n * Created by robevansuk on 15/02/2017.\n */\npublic class Transfer {\n\n    String type; // deposit/withdraw\n    BigDecimal amount;\n    String coinbase_account_id;\n\n    public Transfer() {}\n\n    public Transfer(String type, BigDecimal amount, String coinbase_account_id) {\n        this.type = type;\n        this.amount = amount;\n        this.coinbase_account_id = coinbase_account_id;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public BigDecimal getAmount() {\n        return amount;\n    }\n\n    public void setAmount(BigDecimal amount) {\n        this.amount = amount;\n    }\n\n    public String getCoinbase_account_id() {\n        return coinbase_account_id;\n    }\n\n    public void setCoinbase_account_id(String coinbase_account_id) {\n        this.coinbase_account_id = coinbase_account_id;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/transfers/TransferService.java",
    "content": "package com.coinbase.exchange.api.transfers;\n\nimport com.coinbase.exchange.api.exchange.CoinbaseExchange;\nimport org.springframework.core.ParameterizedTypeReference;\n\nimport java.math.BigDecimal;\n\n/**\n * This class is best used in conjunction with the coinbase library\n * to get the coinbase account Id's. see: https://github.com/coinbase/coinbase-java\n *\n * Created by robevansuk on 15/02/2017.\n */\npublic class TransferService {\n\n    static final String TRANSFER_ENDPOINT = \"/transfers\";\n\n    final CoinbaseExchange coinbaseExchange;\n\n    public TransferService(final CoinbaseExchange coinbaseExchange) {\n        this.coinbaseExchange = coinbaseExchange;\n    }\n\n    /**\n     * TODO untested due to lack of a coinbaseaccountID to test with.\n     * @param type\n     * @param amount\n     * @param coinbaseAccountId\n     * @return\n     */\n    public String transfer(String type, BigDecimal amount, String coinbaseAccountId) {\n        return coinbaseExchange.post(TRANSFER_ENDPOINT,\n                new ParameterizedTypeReference<String>(){},\n                new Transfer(type, amount, coinbaseAccountId));\n    }\n\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/useraccount/UserAccountData.java",
    "content": "package com.coinbase.exchange.api.useraccount;\n\nimport java.math.BigDecimal;\n\n/**\n * Created by robevansuk on 17/02/2017.\n */\npublic class UserAccountData {\n\n    String product_id;\n    BigDecimal exchange_volume;\n    BigDecimal volume;\n    String recorded_at;\n\n    public UserAccountData() {}\n\n    public UserAccountData(String product_id, BigDecimal exchange_volume, BigDecimal volume, String recorded_at) {\n        this.product_id = product_id;\n        this.exchange_volume = exchange_volume;\n        this.volume = volume;\n        this.recorded_at = recorded_at;\n    }\n\n    public String getProduct_id() {\n        return product_id;\n    }\n\n    public void setProduct_id(String product_id) {\n        this.product_id = product_id;\n    }\n\n    public BigDecimal getExchange_volume() {\n        return exchange_volume;\n    }\n\n    public void setExchange_volume(BigDecimal exchange_volume) {\n        this.exchange_volume = exchange_volume;\n    }\n\n    public BigDecimal getVolume() {\n        return volume;\n    }\n\n    public void setVolume(BigDecimal volume) {\n        this.volume = volume;\n    }\n\n    public String getRecorded_at() {\n        return recorded_at;\n    }\n\n    public void setRecorded_at(String recorded_at) {\n        this.recorded_at = recorded_at;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/useraccount/UserAccountService.java",
    "content": "package com.coinbase.exchange.api.useraccount;\n\nimport com.coinbase.exchange.api.exchange.CoinbaseExchange;\nimport org.springframework.core.ParameterizedTypeReference;\n\nimport java.util.List;\n\n/**\n * Created by robevansuk on 17/02/2017.\n */\npublic class UserAccountService {\n\n    private static final String USER_ACCOUNT_ENDPOINT = \"/users/self/trailing-volume\";\n\n    final CoinbaseExchange coinbaseExchange;\n\n    public UserAccountService(final CoinbaseExchange coinbaseExchange) {\n        this.coinbaseExchange = coinbaseExchange;\n    }\n\n    /**\n     * Returns the 30 day trailing volume information from all accounts\n     * @return UserAccountData\n     */\n    public List<UserAccountData> getTrailingVolume(){\n        return coinbaseExchange.getAsList(USER_ACCOUNT_ENDPOINT, new ParameterizedTypeReference<UserAccountData[]>() {});\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/com/coinbase/exchange/api/withdrawals/WithdrawalsService.java",
    "content": "package com.coinbase.exchange.api.withdrawals;\n\nimport com.coinbase.exchange.api.exchange.CoinbaseExchange;\nimport com.coinbase.exchange.model.CoinbasePaymentRequest;\nimport com.coinbase.exchange.model.CryptoPaymentRequest;\nimport com.coinbase.exchange.model.MonetaryRequest;\nimport com.coinbase.exchange.model.PaymentRequest;\nimport com.coinbase.exchange.model.PaymentResponse;\nimport org.springframework.core.ParameterizedTypeReference;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\n\n/**\n * Created by robevansuk on 16/02/2017.\n */\npublic class WithdrawalsService {\n\n    private static final String WITHDRAWALS_ENDPOINT = \"/withdrawals\";\n    private static final String PAYMENT_METHOD = \"/payment-method\";\n    private static final String COINBASE = \"/coinbase-account\";\n    private static final String CRYPTO = \"/crypto\";\n\n    final CoinbaseExchange coinbaseExchange;\n\n    public WithdrawalsService(final CoinbaseExchange coinbaseExchange) {\n        this.coinbaseExchange = coinbaseExchange;\n    }\n\n    public PaymentResponse makeWithdrawalToPaymentMethod(BigDecimal amount, String currency, String paymentMethodId) {\n        PaymentRequest request = new PaymentRequest(amount, currency, paymentMethodId);\n        return makeWithdrawal(request, PAYMENT_METHOD);\n    }\n\n    // TODO untested - needs coinbase account ID to work.\n    public PaymentResponse makeWithdrawalToCoinbase(BigDecimal amount, String currency, String coinbaseAccountId) {\n        CoinbasePaymentRequest request = new CoinbasePaymentRequest(amount.setScale(8, RoundingMode.HALF_DOWN), currency, coinbaseAccountId);\n        return makeWithdrawal(request, COINBASE);\n    }\n\n    // TODO untested - needs a crypto currency account address\n    public PaymentResponse makeWithdrawalToCryptoAccount(BigDecimal amount, String currency, String cryptoAccountAddress) {\n        CryptoPaymentRequest request = new CryptoPaymentRequest(amount.setScale(8, RoundingMode.HALF_DOWN), currency, cryptoAccountAddress);\n        return makeWithdrawal(request, CRYPTO);\n    }\n\n\n    private PaymentResponse makeWithdrawal(MonetaryRequest request, String withdrawalType) {\n        return coinbaseExchange.post(WITHDRAWALS_ENDPOINT+ withdrawalType,\n                new ParameterizedTypeReference<PaymentResponse>() {},\n                request);\n    }\n}\n"
  },
  {
    "path": "api/src/test/java/com/coinbase/exchange/api/BaseIntegrationTest.java",
    "content": "package com.coinbase.exchange.api;\n\nimport com.coinbase.exchange.api.exchange.CoinbaseExchange;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n/**\n * This is an integration test. Tests extending this class should be\n * run it against the Coinbase Pro sandbox API. To do this you will need\n * to provide credentials in resources/application-test.yml\n *\n * Created by robevansuk on 20/01/2017.\n */\n@SpringBootTest(properties = {\n                    \"spring.profiles.active=test\"\n                })\npublic abstract class BaseIntegrationTest {\n\n    @Autowired\n    public CoinbaseExchange exchange;\n}\n"
  },
  {
    "path": "api/src/test/java/com/coinbase/exchange/api/TestExchangeApplication.java",
    "content": "package com.coinbase.exchange.api;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * The main codebase is a library. In order to test it we can\n * instantiate a spring boot application that will wire in the various\n * config from application-test.yaml properties and connect to the exchange.\n * This makes running integration tests relatively simple.\n */\n@SpringBootApplication\npublic class TestExchangeApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TestExchangeApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "api/src/test/java/com/coinbase/exchange/api/accounts/AccountsIntegrationTest.java",
    "content": "package com.coinbase.exchange.api.accounts;\n\nimport com.coinbase.exchange.api.BaseIntegrationTest;\nimport com.coinbase.exchange.api.config.IntegrationTestConfiguration;\nimport com.coinbase.exchange.model.Hold;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * See class doc for BaseIntegrationTest\n *\n * Created by robevansuk on 03/02/2017.\n */\n@ExtendWith(SpringExtension.class)\n@Import({IntegrationTestConfiguration.class})\npublic class AccountsIntegrationTest extends BaseIntegrationTest {\n\n    AccountService accountService;\n\n    @BeforeEach\n    void setUp() {\n        this.accountService = new AccountService(exchange);\n    }\n\n    @Test\n    public void canGetAccounts() {\n        List<Account> accounts  = accountService.getAccounts();\n        assertNotNull(accounts);\n    }\n\n    @Test\n    public void getAccount() {\n        List<Account> accounts  = accountService.getAccounts();\n        Account account = accountService.getAccount(accounts.get(0).getId());\n        assertNotNull(account);\n    }\n\n    @Test\n    public void canGetAccountHistory() {\n        List<Account> accounts = accountService.getAccounts();\n        List<AccountHistory> history = accountService.getAccountHistory(accounts.get(0).getId());\n        assertNotNull(history); // anything but null/error.\n    }\n\n    @Test\n    public void canGetAccountHolds() {\n        List<Account> accounts = accountService.getAccounts();\n        List<Hold> holds = accountService.getHolds(accounts.get(0).getId());\n        assertNotNull(holds);\n        // only check the holds if they exist\n        if (holds.size()>0) {\n            assertTrue(holds.get(0).getAmount().floatValue() >= 0.0f);\n        }\n    }\n\n    /**\n     * note that for paged requests the before param takes precedence\n     * only if before is null and after is not-null will the after param be inserted.\n     */\n    @Test\n    public void canGetPagedAccountHistory() {\n        List<Account> accounts = accountService.getAccounts();\n        assertTrue(accounts.size() > 0);\n        String beforeOrAfter = \"before\";\n        int pageNumber = 1;\n        int limit = 5;\n        List<AccountHistory> firstPageAccountHistory = accountService.getPagedAccountHistory(accounts.get(0).getId(),\n                beforeOrAfter, pageNumber, limit);\n        assertNotNull(firstPageAccountHistory);\n        assertTrue(firstPageAccountHistory.size() <= limit);\n    }\n\n    @Test\n    public void canGetPagedHolds() {\n        List<Account> accounts = accountService.getAccounts();\n\n        assertTrue(accounts!=null);\n        assertTrue(accounts.size() > 0);\n\n        String beforeOrAfter = \"after\";\n        int pageNumber = 1;\n        int limit = 5;\n\n        List<Hold> firstPageOfHolds = accountService.getPagedHolds(accounts.get(0).getId(),\n                beforeOrAfter,\n                pageNumber,\n                limit);\n\n        assertNotNull(firstPageOfHolds);\n        assertTrue(firstPageOfHolds.size() <= limit);\n    }\n}\n"
  },
  {
    "path": "api/src/test/java/com/coinbase/exchange/api/accounts/DepositIntegrationTest.java",
    "content": "package com.coinbase.exchange.api.accounts;\n\nimport com.coinbase.exchange.api.BaseIntegrationTest;\nimport com.coinbase.exchange.api.config.IntegrationTestConfiguration;\nimport com.coinbase.exchange.api.deposits.DepositService;\nimport com.coinbase.exchange.api.payments.CoinbaseAccount;\nimport com.coinbase.exchange.api.payments.PaymentService;\nimport com.coinbase.exchange.model.PaymentResponse;\nimport org.junit.Ignore;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.math.BigDecimal;\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * See class doc for BaseIntegrationTest\n */\n@ExtendWith(SpringExtension.class)\n@Import({IntegrationTestConfiguration.class})\n@Ignore\npublic class DepositIntegrationTest extends BaseIntegrationTest {\n    private final static Logger log = LoggerFactory.getLogger(DepositIntegrationTest.class);\n\n    PaymentService paymentService;\n    AccountService accountService;\n    DepositService testee;\n\n    @BeforeEach\n    void setUp() {\n        this.paymentService = new PaymentService(exchange);\n        this.accountService = new AccountService(exchange);\n        this.testee = new DepositService(exchange);\n    }\n\n    @Test\n    public void depositToGDAXExchangeFromCoinbase(){\n        // given\n        List<CoinbaseAccount> coinbaseAccountList = paymentService.getCoinbaseAccounts();\n        assertTrue(coinbaseAccountList.size() > 0);\n        List<Account> gdaxAccountList = accountService.getAccounts();\n        assertTrue(gdaxAccountList.size() > 0);\n        CoinbaseAccount coinbaseAccount = getCoinbaseAccount(coinbaseAccountList);\n        Account gdaxAccount = getAccount(gdaxAccountList);\n        log.info(\"Testing depositToGDAXExchangeFromCoinbase with \" + coinbaseAccount.getId());\n\n        BigDecimal initGDAXValue = gdaxAccount.getBalance();\n        BigDecimal depositAmount = new BigDecimal(100);\n\n        PaymentResponse response = testee.depositViaCoinbase(depositAmount, coinbaseAccount.getCurrency(), coinbaseAccount.getId());\n        log.info(\"Returned: \" + response.getId());\n\n        // when\n        gdaxAccount = accountService.getAccount(gdaxAccount.getId());\n\n        // then\n        assertEquals(0, initGDAXValue.add(depositAmount).compareTo(gdaxAccount.getBalance()));\n    }\n\n    private Account getAccount(List<Account> gdaxAccountList) {\n        Account gdaxAccount = null;\n        for(Account account: gdaxAccountList){\n            if(account.getCurrency().equalsIgnoreCase(\"USD\")){\n                gdaxAccount = account;\n                break;\n            }\n        }\n        assertNotNull(gdaxAccount);\n        return gdaxAccount;\n    }\n\n    private CoinbaseAccount getCoinbaseAccount(List<CoinbaseAccount> coinbaseAccountList) {\n        CoinbaseAccount coinbaseAccount = null;\n        for(CoinbaseAccount account : coinbaseAccountList){\n            if(account.getCurrency().equalsIgnoreCase(\"USD\")){\n                coinbaseAccount = account;\n                break;\n            }\n        }\n        assertNotNull(coinbaseAccount);\n        return coinbaseAccount;\n    }\n}\n"
  },
  {
    "path": "api/src/test/java/com/coinbase/exchange/api/accounts/UserAccountServiceIntegrationTest.java",
    "content": "package com.coinbase.exchange.api.accounts;\n\nimport com.coinbase.exchange.api.BaseIntegrationTest;\nimport com.coinbase.exchange.api.config.IntegrationTestConfiguration;\nimport com.coinbase.exchange.api.useraccount.UserAccountData;\nimport com.coinbase.exchange.api.useraccount.UserAccountService;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\n/**\n * See class doc for BaseIntegrationTest\n */\n@ExtendWith(SpringExtension.class)\n@Import({IntegrationTestConfiguration.class})\npublic class UserAccountServiceIntegrationTest extends BaseIntegrationTest {\n\n    UserAccountService userAccountService;\n\n    @BeforeEach\n    void setUp() {\n        this.userAccountService = new UserAccountService(exchange);\n    }\n\n    /**\n     * Trailing volume could be empty so all we have to do is make sure it's not returning null\n     */\n    @Test\n    public void getTrailingVolume(){\n        List<UserAccountData> data = userAccountService.getTrailingVolume();\n        assertNotNull(data);\n    }\n}\n"
  },
  {
    "path": "api/src/test/java/com/coinbase/exchange/api/accounts/WithdrawalIntegrationTest.java",
    "content": "package com.coinbase.exchange.api.accounts;\n\nimport com.coinbase.exchange.api.BaseIntegrationTest;\nimport com.coinbase.exchange.api.config.IntegrationTestConfiguration;\nimport com.coinbase.exchange.api.payments.CoinbaseAccount;\nimport com.coinbase.exchange.api.payments.PaymentService;\nimport com.coinbase.exchange.api.payments.PaymentType;\nimport com.coinbase.exchange.api.withdrawals.WithdrawalsService;\nimport com.coinbase.exchange.model.PaymentResponse;\nimport org.junit.Ignore;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.math.BigDecimal;\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * See class doc for BaseIntegrationTest\n */\n@ExtendWith(SpringExtension.class)\n@Import({IntegrationTestConfiguration.class})\n@Ignore\npublic class WithdrawalIntegrationTest extends BaseIntegrationTest {\n\n    private final static Logger log  = LoggerFactory.getLogger(WithdrawalIntegrationTest.class);\n\n    PaymentService paymentService;\n    WithdrawalsService withdrawalsService;\n    AccountService accountService;\n\n    @BeforeEach\n    void setUp() {\n        this.paymentService = new PaymentService(exchange);\n        this.withdrawalsService = new WithdrawalsService(exchange);\n        this.accountService = new AccountService(exchange);\n    }\n\n    @Test\n    public void withdrawToCoinbaseAccount(){\n        List<Account> gdaxAccounts = accountService.getAccounts();\n        List<PaymentType> paymentTypes = paymentService.getPaymentTypes();\n        List<CoinbaseAccount> coinbaseAccounts = paymentService.getCoinbaseAccounts();\n        assertTrue(paymentTypes.size() > 0);\n\n        PaymentType mainType = getUsdPaymentType(paymentTypes);\n        Account gdaxAccount = getUsdAccount(gdaxAccounts);\n        CoinbaseAccount account = getUsdCoinbaseAccount(coinbaseAccounts);\n\n        log.info(\"Testing withdrawToPayment with \" + mainType.getId());\n\n        BigDecimal gdaxUSDValue = gdaxAccount.getBalance();\n        BigDecimal withdrawAmount = new BigDecimal(100);\n        PaymentResponse response = withdrawalsService.makeWithdrawalToCoinbase(withdrawAmount, mainType.getCurrency(), account.getId());\n        assertTrue(response.getId().length() > 0 && response.getAmount().compareTo(withdrawAmount) == 0);\n        log.info(\"Returned: \" + response.getId());\n        gdaxAccount = accountService.getAccount(gdaxAccount.getId());\n        assertEquals(0, gdaxUSDValue.subtract(withdrawAmount).compareTo(gdaxAccount.getBalance()));\n    }\n\n    private CoinbaseAccount getUsdCoinbaseAccount(List<CoinbaseAccount> coinbaseAccounts) {\n        CoinbaseAccount account = null;\n        for(CoinbaseAccount coinbaseAccount : coinbaseAccounts){\n            if(coinbaseAccount.getCurrency().equalsIgnoreCase(\"USD\")){\n                account = coinbaseAccount;\n            }\n        }\n        assertNotNull(account);\n        return account;\n    }\n\n    private Account getUsdAccount(List<Account> gdaxAccounts) {\n        Account gdaxAccount = null;\n        for(Account account : gdaxAccounts){\n            if(account.getCurrency().equalsIgnoreCase(\"USD\")){\n                gdaxAccount = account;\n                break;\n            }\n        }\n        assertNotNull(gdaxAccount);\n        return gdaxAccount;\n    }\n\n    private PaymentType getUsdPaymentType(List<PaymentType> paymentTypes) {\n        PaymentType mainType = null;\n        for(PaymentType paymentType : paymentTypes){\n           if(paymentType.getCurrency().equalsIgnoreCase(\"USD\")){\n               mainType = paymentType;\n               break;\n           }\n        }\n        assertNotNull(mainType);\n        return mainType;\n    }\n}\n"
  },
  {
    "path": "api/src/test/java/com/coinbase/exchange/api/authentication/AuthenticationIntegrationIntegrationTest.java",
    "content": "package com.coinbase.exchange.api.authentication;\n\nimport com.coinbase.exchange.api.BaseIntegrationTest;\nimport com.coinbase.exchange.api.accounts.Account;\nimport com.coinbase.exchange.api.accounts.AccountService;\nimport com.coinbase.exchange.api.config.IntegrationTestConfiguration;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * See class doc for BaseIntegrationTest\n *\n * Created by irufus (sakamura@gmail.com)\n * @Description The primary function of this class is to run through basic tests for the Authentication and GdaxExchange classes\n */\n@ExtendWith(SpringExtension.class)\n@Import({IntegrationTestConfiguration.class})\npublic class AuthenticationIntegrationIntegrationTest extends BaseIntegrationTest {\n\n    AccountService accountService;\n\n    @BeforeEach\n    void setUp() {\n        accountService = new AccountService(exchange);\n    }\n\n    // ensure a basic request can be made. Not a great test. Improve.\n    @Test\n    public void simpleAuthenticationTest(){\n        List<Account> accounts = accountService.getAccounts();\n        assertNotNull(accounts);\n        assertTrue(accounts.size() > 0);\n    }\n\n}\n"
  },
  {
    "path": "api/src/test/java/com/coinbase/exchange/api/config/IntegrationTestConfiguration.java",
    "content": "package com.coinbase.exchange.api.config;\n\nimport com.coinbase.exchange.api.exchange.CoinbaseExchange;\nimport com.coinbase.exchange.api.exchange.CoinbaseExchangeImpl;\nimport com.coinbase.exchange.security.Signature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.SpringBootConfiguration;\nimport org.springframework.context.annotation.Bean;\n\n@SpringBootConfiguration\npublic class IntegrationTestConfiguration {\n\n    @Bean\n    public ObjectMapper objectMapper() {\n        return new ObjectMapper().registerModule(new JavaTimeModule());\n    }\n\n    @Bean\n    public CoinbaseExchange coinbaseExchange(@Value(\"${exchange.key}\") String apiKey,\n                                             @Value(\"${exchange.passphrase}\") String passphrase,\n                                             @Value(\"${exchange.api.baseUrl}\") String baseUrl,\n                                             @Value(\"${exchange.secret}\") String secretKey,\n                                             ObjectMapper objectMapper) {\n        return new CoinbaseExchangeImpl(apiKey,\n                passphrase,\n                baseUrl,\n                new Signature(secretKey),\n                objectMapper);\n    }\n\n}\n"
  },
  {
    "path": "api/src/test/java/com/coinbase/exchange/api/marketdata/MarketDataIntegrationTest.java",
    "content": "package com.coinbase.exchange.api.marketdata;\n\nimport com.coinbase.exchange.api.BaseIntegrationTest;\nimport com.coinbase.exchange.api.config.IntegrationTestConfiguration;\nimport com.coinbase.exchange.api.products.ProductService;\nimport com.coinbase.exchange.model.Product;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * See class doc for BaseIntegrationTest\n *\n * Created by robevansuk on 14/02/2017.\n */\n@ExtendWith(SpringExtension.class)\n@Import({IntegrationTestConfiguration.class})\npublic class MarketDataIntegrationTest extends BaseIntegrationTest {\n\n    ProductService productService;\n    MarketDataService testee;\n\n    @BeforeEach\n    void setUp() {\n        productService = new ProductService(exchange);\n        testee = new MarketDataService(exchange);\n    }\n\n    @Test\n    public void canGetMarketDataForLevelOneBidAndAsk() {\n        MarketData marketData = testee.getMarketDataOrderBook(\"BTC-GBP\", 1);\n        System.out.println(marketData);\n        assertTrue(marketData.getSequence() > 0);\n    }\n\n    @Test\n    public void canGetMarketDataForLevelTwoBidAndAsk() {\n        MarketData marketData = testee.getMarketDataOrderBook(\"BTC-GBP\", 2);\n        System.out.println(marketData);\n        assertTrue(marketData.getSequence() > 0);\n    }\n\n    /**\n     * note that the returned results are slightly different for level 3. For level 3 you will see an\n     * order Id rather than the count of orders at a certain price.\n     */\n    @Test\n    public void canGetMarketDataForLevelThreeBidAndAsk() {\n        MarketData marketData = testee.getMarketDataOrderBook(\"BTC-GBP\", 3);\n        System.out.println(marketData);\n        assertTrue(marketData.getSequence() > 0);\n    }\n\n    @Test\n    public void canGetLevel1DataForAllProducts(){\n        List<Product> products = productService.getProducts();\n        for(Product product : products){\n            System.out.print(\"\\nTesting: \" + product.getId());\n            MarketData data = testee.getMarketDataOrderBook(product.getId(), 1);\n            assertNotNull(data);\n\n            if(data.getBids().size() > 0 && data.getAsks().size() > 0) {\n                System.out.print(\" B: \" + data.getBids().get(0).getPrice() + \" A: \" + data.getAsks().get(0).getPrice());\n            } else {\n                System.out.print(\" NO DATA \");\n            }\n            try {\n                Thread.sleep(1000);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "api/src/test/java/com/coinbase/exchange/api/marketdata/OrderItemDeserializerTest.java",
    "content": "package com.coinbase.exchange.api.marketdata;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.IOException;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * Created by ren7881 on 20/03/2017.\n */\npublic class OrderItemDeserializerTest {\n\n    private ObjectMapper mapper = new ObjectMapper();\n\n    /**\n     * 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.\n     * @throws IOException\n     */\n    @Test\n    public void testDesirialization() throws IOException {\n        String test = \"{\\n\" +\n                \"    \\\"sequence\\\": \\\"3\\\",\\n\" +\n                \"    \\\"bids\\\": [\\n\" +\n                \"        [ \\\"111.96\\\", \\\"2.11111\\\", 3 ],\\n\" +\n                \"        [ \\\"295.96\\\", \\\"4.39088265\\\", 2 ]\\n\" +\n                \"    ],\\n\" +\n                \"    \\\"asks\\\": [\\n\" +\n                \"        [ \\\"555.97\\\", \\\"66.5656565\\\", 10 ],\\n\" +\n                \"        [ \\\"295.97\\\", \\\"25.23542881\\\", 12 ]\\n\" +\n                \"    ]\\n\" +\n                \"}\";\n\n        MarketData marketData = mapper.readValue(test, MarketData.class);\n        assertEquals(marketData.getAsks().size(), 2);\n        assertEquals(marketData.getBids().size(), 2);\n        assertEquals(marketData.getSequence(), 3L);\n    }\n}"
  },
  {
    "path": "api/src/test/java/com/coinbase/exchange/api/orders/OrderIntegrationTest.java",
    "content": "package com.coinbase.exchange.api.orders;\n\nimport com.coinbase.exchange.api.BaseIntegrationTest;\nimport com.coinbase.exchange.api.accounts.Account;\nimport com.coinbase.exchange.api.accounts.AccountService;\nimport com.coinbase.exchange.api.config.IntegrationTestConfiguration;\nimport com.coinbase.exchange.api.marketdata.MarketData;\nimport com.coinbase.exchange.api.marketdata.MarketDataService;\nimport com.coinbase.exchange.api.products.ProductService;\nimport com.coinbase.exchange.model.Fill;\nimport com.coinbase.exchange.model.NewLimitOrderSingle;\nimport com.coinbase.exchange.model.NewMarketOrderSingle;\nimport org.junit.Ignore;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.util.List;\nimport java.util.Optional;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * See class doc for BaseIntegrationTest\n *\n * Created by Ishmael (sakamura@gmail.com) on 6/18/2016.\n */\n@ExtendWith(SpringExtension.class)\n@Import({IntegrationTestConfiguration.class})\npublic class OrderIntegrationTest extends BaseIntegrationTest {\n\n    private static final Logger log = LoggerFactory.getLogger(OrderIntegrationTest.class);\n\n    ProductService productService;\n    AccountService accountService;\n    MarketDataService marketDataService;\n\n    OrderService testee;\n\n    // accounts: BTC, USD, GBP, EUR, CAD\n    // products: BTC-USD, BTC-GBP, BTC-EUR, ETH-BTC, ETH-USD, LTC-BTC, LTC-USD\n\n\n    @BeforeEach\n    void setUp() {\n        this.productService = new ProductService(exchange);\n        this.accountService = new AccountService(exchange);\n        this.marketDataService = new MarketDataService(exchange);\n        this.testee = new OrderService(exchange);\n    }\n\n    /**\n     * Test is too complex. This test tests placing an order and\n     * then cancelling it without leaving a mess.\n     * Note: You'll need credit available in your test account\n     */\n    @Ignore\n    public void canMakeLimitOrderAndGetTheOrderAndCancelIt() {\n        List<Account> accounts = accountService.getAccounts();\n        Optional<Account> accountsWithMoreThanZeroCoinsAvailable = accounts.stream()\n                .filter(account -> account.getBalance().compareTo(BigDecimal.ONE) > 0 && account.getCurrency().contains(\"BTC\"))\n                .findFirst();\n\n        assertTrue(accountsWithMoreThanZeroCoinsAvailable.isPresent());\n\n        String productId;\n        if (accountsWithMoreThanZeroCoinsAvailable.get().getCurrency().equals(\"BTC\")) {\n            productId = accountsWithMoreThanZeroCoinsAvailable.get().getCurrency() + \"-USD\";\n        } else {\n            productId = accountsWithMoreThanZeroCoinsAvailable.get().getCurrency() + \"-BTC\";\n        }\n\n        MarketData marketData = getMarketDataOrderBook(productId);\n\n        assertNotNull(marketData);\n\n        BigDecimal price = getAskPrice(marketData).setScale(8, RoundingMode.HALF_UP);\n        BigDecimal size = new BigDecimal(\"0.01\").setScale(8, RoundingMode.HALF_UP);\n\n        NewLimitOrderSingle limitOrder = getNewLimitOrderSingle(productId, price, size);\n\n        Order order = testee.createOrder(limitOrder);\n\n        assertNotNull(order);\n        assertEquals(productId, order.getProduct_id());\n        assertEquals(size, new BigDecimal(order.getSize()).setScale(8, RoundingMode.HALF_UP));\n        assertEquals(price, new BigDecimal(order.getPrice()).setScale(8, RoundingMode.HALF_UP));\n        assertEquals(\"limit\", order.getType());\n\n        testee.cancelOrder(order.getId());\n        List<Order> orders = testee.getOpenOrders();\n        orders.stream().forEach(o -> assertTrue(o.getId() != order.getId()));\n    }\n\n    @Test\n    public void cancelAllOrders() {\n        List<Order> cancelledOrders = testee.cancelAllOpenOrders();\n        assertTrue(cancelledOrders.size() >= 0);\n    }\n\n    @Test\n    public void getAllOpenOrders() {\n        List<Order> openOrders = testee.getOpenOrders();\n        assertTrue(openOrders.size() >= 0);\n    }\n\n    @Test\n    public void getFillsByProductId() {\n        List<Fill> fills = testee.getFillsByProductId(\"BTC-USD\", 100);\n        assertTrue(fills.size() >= 0);\n    }\n\n    @Ignore\n    public void shouldGetFilledByOrderIdWhenMakingMarketOrderBuy() {\n        NewMarketOrderSingle marketOrder = createNewMarketOrder(\"BTC-USD\", \"buy\", new BigDecimal(0.01));\n        Order order = testee.createOrder(marketOrder);\n\n        List<Fill> fills = testee.getFillByOrderId(order.getId(), 100);\n\n        assertEquals(1, fills.size());\n    }\n\n    @Ignore\n    public void createMarketOrderBuy() {\n        NewMarketOrderSingle marketOrder = createNewMarketOrder(\"BTC-USD\", \"buy\", new BigDecimal(0.01));\n        Order order = testee.createOrder(marketOrder);\n\n        assertNotNull(order); //make sure we created an order\n        String orderId = order.getId();\n        assertTrue(orderId.length() > 0); //ensure we have an actual orderId\n        Order filledOrder = testee.getOrder(orderId);\n        assertNotNull(filledOrder); //ensure our order hit the system\n        assertTrue(new BigDecimal(filledOrder.getSize()).compareTo(BigDecimal.ZERO) > 0); //ensure we got a fill\n        log.info(\"Order opened and filled: \" + filledOrder.getSize() + \" @ \" + filledOrder.getExecuted_value()\n                + \" at the cost of \" + filledOrder.getFill_fees());\n    }\n\n    @Ignore\n    public void createMarketOrderSell() {\n        NewMarketOrderSingle marketOrder = createNewMarketOrder(\"BTC-USD\", \"sell\", new BigDecimal(0.01));\n        Order order = testee.createOrder(marketOrder);\n        assertNotNull(order); //make sure we created an order\n        String orderId = order.getId();\n        assertTrue(orderId.length() > 0); //ensure we have an actual orderId\n        Order filledOrder = testee.getOrder(orderId);\n        assertNotNull(filledOrder); //ensure our order hit the system\n        assertTrue(new BigDecimal(filledOrder.getSize()).compareTo(BigDecimal.ZERO) > 0); //ensure we got a fill\n        log.info(\"Order opened and filled: \" + filledOrder.getSize() + \" @ \" + filledOrder.getExecuted_value()\n                + \" at the cost of \" + filledOrder.getFill_fees());\n    }\n\n    private NewMarketOrderSingle createNewMarketOrder(String product, String action, BigDecimal size) {\n        NewMarketOrderSingle marketOrder = new NewMarketOrderSingle();\n        marketOrder.setProduct_id(product);\n        marketOrder.setSide(action);\n        marketOrder.setSize(size);\n        return marketOrder;\n    }\n\n    private MarketData getMarketDataOrderBook(String product) {\n        return marketDataService.getMarketDataOrderBook(product, 1);\n    }\n\n    private NewLimitOrderSingle getNewLimitOrderSingle(String productId, BigDecimal price, BigDecimal size) {\n        NewLimitOrderSingle limitOrder = new NewLimitOrderSingle();\n        limitOrder.setProduct_id(productId);\n        if (productId.contains(\"-BTC\")) {\n            limitOrder.setSide(\"sell\");\n        } else {\n            limitOrder.setSide(\"buy\");\n        }\n        limitOrder.setType(\"limit\");\n        limitOrder.setPrice(price);\n        limitOrder.setSize(size);\n        return limitOrder;\n    }\n\n    private BigDecimal getAskPrice(MarketData marketData) {\n        return marketData.getAsks().get(0).getPrice().setScale(4, RoundingMode.HALF_UP);\n    }\n\n    /**\n     * @param accountsAvailable Available accounts to trade from\n     * @return null or String\n     */\n    private MarketData getTradeableProductData(List<Account> accountsAvailable) {\n        MarketData data = null;\n        for (Account account : accountsAvailable) {\n            System.out.println(\"Do nothing\");\n        }\n        return data;\n    }\n}\n"
  },
  {
    "path": "api/src/test/java/com/coinbase/exchange/api/payments/PaymentIntegrationTest.java",
    "content": "package com.coinbase.exchange.api.payments;\n\nimport com.coinbase.exchange.api.BaseIntegrationTest;\nimport com.coinbase.exchange.api.config.IntegrationTestConfiguration;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n@ExtendWith(SpringExtension.class)\n@Import({IntegrationTestConfiguration.class})\npublic class PaymentIntegrationTest extends BaseIntegrationTest {\n\n    PaymentService testee;\n\n    @BeforeEach\n    void setUp() {\n        testee = new PaymentService(exchange);\n    }\n\n    @Test\n    public void hasAvailablePayments(){\n        List<PaymentType> types = testee.getPaymentTypes();\n        assertTrue(types.size() > 0);\n    }\n    @Test\n    public void hasCoinbaseAccounts(){\n        List<CoinbaseAccount> accounts = testee.getCoinbaseAccounts();\n        assertTrue(accounts.size() > 0);\n    }\n}\n"
  },
  {
    "path": "api/src/test/java/com/coinbase/exchange/api/products/ProductsIntegrationTest.java",
    "content": "package com.coinbase.exchange.api.products;\n\nimport com.coinbase.exchange.api.BaseIntegrationTest;\nimport com.coinbase.exchange.api.config.IntegrationTestConfiguration;\nimport com.coinbase.exchange.model.Candles;\nimport com.coinbase.exchange.model.Granularity;\nimport com.coinbase.exchange.model.Product;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * See class doc for BaseIntegrationTest\n * <p>\n * Created by robevansuk on 08/02/2017.\n */\n@ExtendWith(SpringExtension.class)\n@Import({IntegrationTestConfiguration.class})\npublic class ProductsIntegrationTest extends BaseIntegrationTest {\n\n    private static final String TEST_PRODUCT_ID = \"BTC-GBP\";\n    public static final int ONE_DAY_IN_SECONDS = 60 * 60 * 24;\n    public static final int SIX_HOURS_IN_SECONDS = 60 * 60 * 6;\n    public static final int ONE_HOUR_IN_SECONDS = 60 * 60;\n    public static final int FIFTEEN_MINS_IN_SECONDS = 60 * 15;\n    public static final int FIVE_MINS_IN_SECONDS = 60 * 5;\n    public static final int ONE_MIN_IN_SECONDS = 60;\n\n    private ProductService productService;\n\n    @BeforeEach\n    void setUp() {\n        this.productService = new ProductService(exchange);\n    }\n\n    @Test\n    public void canGetProducts() {\n        List<Product> products = productService.getProducts();\n        products.forEach(item -> System.out.println(item.getId()));\n        assertTrue(products.size() >= 0);\n    }\n\n    @Test\n    void shouldGetCandles() {\n        Candles candles = productService.getCandles(TEST_PRODUCT_ID);\n\n        assertEquals(300, candles.getCandleList().size());\n    }\n\n    @Test\n    void shouldGetCandlesForAGanularityOf_OneDay() {\n        Candles candles = productService.getCandles(TEST_PRODUCT_ID, Granularity.ONE_DAY);\n\n        assertEquals(300, candles.getCandleList().size());\n        assertEquals(ONE_DAY_IN_SECONDS, getDuration(candles).getSeconds());\n    }\n\n    @Test\n    void shouldGetCandlesForAGanularityOf_SixHours() {\n        Candles candles = productService.getCandles(TEST_PRODUCT_ID, Granularity.SIX_HOURS);\n\n        assertEquals(300, candles.getCandleList().size());\n        assertEquals(SIX_HOURS_IN_SECONDS, getDuration(candles).getSeconds());\n    }\n\n    @Test\n    void shouldGetCandlesForAGanularityOf_OneHour() {\n        Candles candles = productService.getCandles(TEST_PRODUCT_ID, Granularity.ONE_HOUR);\n\n        assertEquals(300, candles.getCandleList().size());\n        assertEquals(ONE_HOUR_IN_SECONDS, getDuration(candles).getSeconds());\n    }\n\n    @Test\n    void shouldGetCandlesForAGanularityOf_FifteenMins() {\n        Candles candles = productService.getCandles(TEST_PRODUCT_ID, Granularity.FIFTEEN_MIN);\n\n        assertEquals(300, candles.getCandleList().size());\n        assertEquals(FIFTEEN_MINS_IN_SECONDS, getDuration(candles).getSeconds());\n    }\n\n    @Test\n    void shouldGetCandlesForAGanularityOf_FiveMins() {\n        Candles candles = productService.getCandles(TEST_PRODUCT_ID, Granularity.FIVE_MIN);\n\n        assertEquals(300, candles.getCandleList().size());\n        assertEquals(FIVE_MINS_IN_SECONDS, getDuration(candles).getSeconds());\n    }\n\n    @Test\n    void shouldGetCandlesForAGanularityOf_OneMin() {\n        Candles candles = productService.getCandles(TEST_PRODUCT_ID, Granularity.ONE_MIN);\n\n        assertEquals(300, candles.getCandleList().size());\n        assertEquals(ONE_MIN_IN_SECONDS, getDuration(candles).getSeconds());\n    }\n\n    @Test\n    void shouldGetCandlesForWithAStartAndEndTime() {\n        Instant startTime = Instant.now().minus(400, ChronoUnit.DAYS);\n        Instant endTime = Instant.now().minus(100, ChronoUnit.DAYS);\n\n        Candles candles = productService.getCandles(TEST_PRODUCT_ID, startTime, endTime, Granularity.ONE_DAY);\n\n        // can't predict how many candles we'll get back but going back more than 300 days\n        // doesn't return 300 candles here.. easiest just to assert we get more than 100 back.\n        assertTrue(candles.getCandleList().size() > 100);\n    }\n\n    private Duration getDuration(Candles candles) {\n        Instant candleTime1 = candles.getCandleList().get(0).getTime();\n        Instant candleTime2 = candles.getCandleList().get(1).getTime();\n        return Duration.between(candleTime2, candleTime1);\n    }\n}\n"
  },
  {
    "path": "api/src/test/java/com/coinbase/exchange/api/transfers/TransferServiceIntegrationTest.java",
    "content": "package com.coinbase.exchange.api.transfers;\n\nimport com.coinbase.exchange.api.BaseIntegrationTest;\nimport com.coinbase.exchange.api.config.IntegrationTestConfiguration;\nimport org.junit.Ignore;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\n/**\n * See class doc for BaseIntegrationTest\n *\n * Created by robevansuk on 15/02/2017.\n */\n@ExtendWith(SpringExtension.class)\n@Import({IntegrationTestConfiguration.class})\npublic class TransferServiceIntegrationTest extends BaseIntegrationTest {\n\n    private TransferService transferService;\n\n    @BeforeEach\n    void setUp() {\n        this.transferService = new TransferService(exchange);\n    }\n\n\n    @Ignore\n    public void canTransferFromCoinbaseAccountToGdax() {\n        // TODO\n    }\n}\n"
  },
  {
    "path": "api/src/test/resources/application-test.yml",
    "content": "exchange:\n  api:\n    baseUrl: \"https://api-public.sandbox.pro.coinbase.com\"\n  key: \"\"\n  secret: \"\"\n  passphrase: \"\"\n\nwebsocket:\n  baseUrl: \"wss://ws-feed-public.sandbox.pro.coinbase.com\"\n\ngui:\n  enabled: true"
  },
  {
    "path": "build.gradle",
    "content": "buildscript {\n    repositories {\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.github.jengelman.gradle.plugins:shadow:5.1.0'\n    }\n}\n\napply plugin: 'com.github.johnrengelman.shadow'\n\nwrapper {\n    distributionType = Wrapper.DistributionType.ALL\n}\n\nallprojects {\n    apply plugin: 'java'\n\n    group = 'com.coinbase.exchange'\n    description = 'Client for the Coinbase Pro API'\n    version = '0.11.0'\n\n    repositories {\n        mavenCentral()\n    }\n\n    test {\n        exclude '**/*IntegrationTest*'\n    }\n\n    /**\n     * User can run these as optional against the sandbox api exchange\n     **/\n    task integrationTest(type: Test) {\n        include '**/*IntegrationTest*'\n    }\n}\n\nsubprojects { }\n\n// apply dependency constraints to all java modules\nconfigure(subprojects.findAll { true }) {\n\n    sourceCompatibility = 1.11\n    targetCompatibility = 1.11\n\n    dependencies {\n        constraints {\n            implementation 'com.fasterxml.jackson.core:jackson-core:2.11.0'\n            implementation 'com.fasterxml.jackson.core:jackson-databind:2.11.0'\n            implementation 'com.fasterxml.jackson.core:jackson-annotations:2.11.0'\n            implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.0'\n            implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.0'\n            implementation 'org.slf4j:slf4j-api:1.7.28'\n            implementation 'ch.qos.logback:logback-classic:1.2.3'\n            implementation 'ch.qos.logback:logback-core:1.2.3'\n            implementation 'org.springframework.boot:spring-boot-starter-web:2.2.7.RELEASE'\n            implementation 'org.springframework:spring-context:5.2.6.RELEASE'\n\n            // --------------------------------------\n            testImplementation 'junit:junit:4.12'\n            testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'\n            testImplementation 'org.junit.jupiter:junit-jupiter-params:5.5.2'\n            testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.5.2'\n            testImplementation 'org.junit.platform:junit-platform-suite-api:1.3.2'\n\n            testImplementation 'org.springframework.boot:spring-boot-test:2.2.7.RELEASE'\n            testImplementation 'org.springframework.boot:spring-boot-starter-test:2.2.7.RELEASE'\n\n            testImplementation 'org.mockito:mockito-core:3.0.0'\n            testImplementation 'org.mockito:mockito-junit-jupiter:3.0.0'\n\n            testImplementation 'org.assertj:assertj-core:3.13.2'\n        }\n    }\n}\n\ndependencies {\n    compile project(\":api\")\n    compile project(\":model\")\n    compile project(\":security\")\n    compile project(\":websocketfeed\")\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Fri Jan 20 14:01:00 GMT 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-5.3.1-all.zip\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave ( ) {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windows variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "model/build.gradle",
    "content": "dependencies {\n    implementation 'com.fasterxml.jackson.core:jackson-core'\n    implementation 'com.fasterxml.jackson.core:jackson-databind'\n    implementation 'com.fasterxml.jackson.core:jackson-annotations'\n    implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8'\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/Candle.java",
    "content": "package com.coinbase.exchange.model;\n\nimport java.math.BigDecimal;\nimport java.time.Instant;\n\npublic class Candle {\n\n    private Instant time;\n    private BigDecimal low;\n    private BigDecimal high;\n    private BigDecimal open;\n    private BigDecimal close;\n    private BigDecimal volume;\n\n    public Candle(String[] entry) {\n        this(Instant.ofEpochSecond(Long.parseLong(entry[0])),\n                new BigDecimal(entry[1]),\n                new BigDecimal(entry[2]),\n                new BigDecimal(entry[3]),\n                new BigDecimal(entry[4]),\n                new BigDecimal(entry[5]));\n    }\n\n    public Candle(Instant time, BigDecimal low, BigDecimal high, BigDecimal open, BigDecimal close, BigDecimal volume) {\n        this.time   = time;\n        this.low    = low;\n        this.high   = high;\n        this.open   = open;\n        this.close  = close;\n        this.volume = volume;\n    }\n\n    public Instant getTime() {\n        return time;\n    }\n\n    public BigDecimal getLow() {\n        return low;\n    }\n\n    public BigDecimal getHigh() {\n        return high;\n    }\n\n    public BigDecimal getOpen() {\n        return open;\n    }\n\n    public BigDecimal getClose() {\n        return close;\n    }\n\n    public BigDecimal getVolume() {\n        return volume;\n    }\n\n    public void setTime(Instant time) {\n        this.time = time;\n    }\n\n    public void setLow(BigDecimal low) {\n        this.low = low;\n    }\n\n    public void setHigh(BigDecimal high) {\n        this.high = high;\n    }\n\n    public void setOpen(BigDecimal open) {\n        this.open = open;\n    }\n\n    public void setClose(BigDecimal close) {\n        this.close = close;\n    }\n\n    public void setVolume(BigDecimal volume) {\n        this.volume = volume;\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/Candles.java",
    "content": "package com.coinbase.exchange.model;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class Candles {\n\n    List<Candle> candleList;\n\n    public Candles(List<String[]> candles) {\n        this.candleList = candles.stream().map(Candle::new).collect(Collectors.toList());\n    }\n\n    public List<Candle> getCandleList() {\n        return candleList;\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/CoinbasePaymentRequest.java",
    "content": "package com.coinbase.exchange.model;\n\nimport java.math.BigDecimal;\n\n/**\n * Created by robevansuk on 15/02/2017.\n */\npublic class CoinbasePaymentRequest extends MonetaryRequest {\n\n    private String coinbase_account_id;\n    private String payment_method_id;\n\n    public CoinbasePaymentRequest(BigDecimal amount, String currency, String coinbase_account_id) {\n        super(amount, currency);\n        this.coinbase_account_id = coinbase_account_id;\n        this.payment_method_id = coinbase_account_id; //Duplicated field for gdax compliance, I believe\n        //We could probably remove coinbase_account_id but there are no tests for this specific thing\n    }\n    public String getCoinbase_account_id() {\n        return coinbase_account_id;\n    }\n    public void setCoinbase_account_id(String coinbase_account_id) {\n        this.coinbase_account_id = coinbase_account_id;\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/CryptoPaymentRequest.java",
    "content": "package com.coinbase.exchange.model;\n\nimport java.math.BigDecimal;\n\npublic class CryptoPaymentRequest extends MonetaryRequest {\n    private String crypto_address;\n\n    public CryptoPaymentRequest(BigDecimal amount, String currency, String cryptoAddress) {\n        super(amount, currency);\n        this.crypto_address = cryptoAddress;\n    }\n    public String getCryptoAddress() {\n        return crypto_address;\n    }\n    public void setCryptoAddress(String cryptoAddress) {\n        this.crypto_address = cryptoAddress;\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/Currency.java",
    "content": "package com.coinbase.exchange.model;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\n\nimport java.math.BigDecimal;\n\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic class Currency {\n    private String id;\n    private String name;\n    private BigDecimal min_size;\n    private String status;\n    private String status_message;\n    private BigDecimal max_precision;\n    private String[] convertible_to;\n    private String funding_account_id;\n    private Object details;\n\n    public Currency() {\n    }\n\n    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) {\n        this();\n        this.id = id;\n        this.name = name;\n        this.min_size = min_size;\n        this.status = status;\n        this.status_message = status_message;\n        this.max_precision = max_precision;\n        this.convertible_to = convertible_to;\n        this.funding_account_id = funding_account_id;\n        this.details = details;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public BigDecimal getMin_size() {\n        return min_size;\n    }\n\n    public void setMin_size(BigDecimal min_size) {\n        this.min_size = min_size;\n    }\n\n    public String getStatus() {\n        return status;\n    }\n\n    public void setStatus(String status) {\n        this.status = status;\n    }\n\n    public String getStatus_message() {\n        return status_message;\n    }\n\n    public void setStatus_message(String status_message) {\n        this.status_message = status_message;\n    }\n\n    public BigDecimal getMax_precision() {\n        return max_precision;\n    }\n\n    public void setMax_precision(BigDecimal max_precision) {\n        this.max_precision = max_precision;\n    }\n\n    public String[] getConvertible_to() {\n        return convertible_to;\n    }\n\n    public void setConvertible_to(String[] convertible_to) {\n        this.convertible_to = convertible_to;\n    }\n\n    public String getFunding_account_id() {\n        return funding_account_id;\n    }\n\n    public void setFunding_account_id(String funding_account_id) {\n        this.funding_account_id = funding_account_id;\n    }\n\n    public Object getDetails() {\n        return details;\n    }\n\n    public void setDetails(Object details) {\n        this.details = details;\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/Detail.java",
    "content": "package com.coinbase.exchange.model;\n\n/**\n * Created by irufus on 2/25/15.\n */\npublic class Detail {\n    private String order_id;\n    private Integer trade_id;\n    private String product_id;\n\n    public String getOrder_id() {\n        return order_id;\n    }\n\n    public void setOrder_id(String order_id) {\n        this.order_id = order_id;\n    }\n\n    public Integer getTrade_id() {\n        return trade_id;\n    }\n\n    public void setTrade_id(Integer trade_id) {\n        this.trade_id = trade_id;\n    }\n\n    public String getProduct_id() {\n        return product_id;\n    }\n\n    public void setProduct_id(String product_id) {\n        this.product_id = product_id;\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/Fill.java",
    "content": "package com.coinbase.exchange.model;\n\nimport java.math.BigDecimal;\n\n/**\n * Created by irufus on 2/18/15.\n */\npublic class Fill {\n    private Integer trade_id;\n    private String product_id;\n    private BigDecimal size;\n    private String order_id;\n    private String created_at;\n    private String liquidity;\n    private BigDecimal fee;\n    private Boolean settled;\n    private String side;\n\n    public String getSide() {\n        return side;\n    }\n\n    public void setSide(String side) {\n        this.side = side;\n    }\n\n    public Boolean getSettled() {\n        return settled;\n    }\n\n    public void setSettled(Boolean settled) {\n        this.settled = settled;\n    }\n\n    public BigDecimal getFee() {\n        return fee;\n    }\n\n    public void setFee(BigDecimal fee) {\n        this.fee = fee;\n    }\n\n    public String getCreated_at() {\n        return created_at;\n    }\n\n    public void setCreated_at(String created_at) {\n        this.created_at = created_at;\n    }\n\n    public String getLiquidity() {\n        return liquidity;\n    }\n\n    public void setLiquidity(String liquidity) {\n        this.liquidity = liquidity;\n    }\n\n    public String getOrder_id() {\n        return order_id;\n    }\n\n    public void setOrder_id(String order_id) {\n        this.order_id = order_id;\n    }\n\n    public BigDecimal getSize() {\n        return size;\n    }\n\n    public void setSize(BigDecimal size) {\n        this.size = size;\n    }\n\n    public String getProduct_id() {\n        return product_id;\n    }\n\n    public void setProduct_id(String product_id) {\n        this.product_id = product_id;\n    }\n\n    public Integer getTrade_id() {\n        return trade_id;\n    }\n\n    public void setTrade_id(Integer trade_id) {\n        this.trade_id = trade_id;\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/Granularity.java",
    "content": "package com.coinbase.exchange.model;\n\npublic enum Granularity {\n    ONE_DAY(\"1d\"),\n    SIX_HOURS(\"6h\"),\n    ONE_HOUR(\"1h\"),\n    FIFTEEN_MIN(\"15m\"),\n    FIVE_MIN(\"5m\"),\n    ONE_MIN(\"1m\");\n\n    private String granularity;\n\n    Granularity(String granularity) {\n        this.granularity = granularity;\n    }\n\n    /**\n     * The granularity field must be one of the following values:\n     * {60, 300, 900, 3600, 21600, 86400}.\n     */\n    public String get(){\n        switch(granularity) {\n            case \"1d\": return \"86400\";\n            case \"6h\": return \"21600\";\n            case \"1h\": return \"3600\";\n            case \"15m\": return \"900\";\n            case \"5m\": return \"300\";\n            case \"1m\": return \"60\";\n        }\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/Hold.java",
    "content": "package com.coinbase.exchange.model;\n\nimport java.math.BigDecimal;\n\n/**\n * Created by irufus on 2/18/15.\n * Updated by robevansuk on 17/2/17\n */\npublic class Hold {\n    String id;\n    String account_id;\n    String created_at;\n    String update_at;\n    BigDecimal amount;\n    String type;\n    String ref;\n\n    public Hold () {}\n\n    public Hold(String id, String account_id, String created_at, String update_at, BigDecimal amount, String type, String ref) {\n        this.id = id;\n        this.account_id = account_id;\n        this.created_at = created_at;\n        this.update_at = update_at;\n        this.amount = amount;\n        this.type = type;\n        this.ref = ref;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getAccount_id() {\n        return account_id;\n    }\n\n    public void setAccount_id(String account_id) {\n        this.account_id = account_id;\n    }\n\n    public String getCreated_at() {\n        return created_at;\n    }\n\n    public void setCreated_at(String created_at) {\n        this.created_at = created_at;\n    }\n\n    public String getUpdate_at() {\n        return update_at;\n    }\n\n    public void setUpdate_at(String update_at) {\n        this.update_at = update_at;\n    }\n\n    public BigDecimal getAmount() {\n        return amount;\n    }\n\n    public void setAmount(BigDecimal amount) {\n        this.amount = amount;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public String getRef() {\n        return ref;\n    }\n\n    public void setRef(String ref) {\n        this.ref = ref;\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/MonetaryRequest.java",
    "content": "package com.coinbase.exchange.model;\n\nimport java.math.BigDecimal;\n\npublic abstract class MonetaryRequest {\n    protected BigDecimal amount;\n    protected String currency;\n\n    public MonetaryRequest(BigDecimal amount, String currency){\n        this.amount = amount;\n        this.currency = currency;\n    }\n\n    public BigDecimal getAmount() {\n        return amount;\n    }\n\n    public void setAmount(BigDecimal amount) {\n        this.amount = amount;\n    }\n\n    public String getCurrency() {\n        return currency;\n    }\n\n    public void setCurrency(String currency) {\n        this.currency = currency;\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/NewLimitOrderSingle.java",
    "content": "package com.coinbase.exchange.model;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\n\n/**\n * Created by irufus on 7/31/15.\n */\npublic class NewLimitOrderSingle extends NewOrderSingle {\n    private BigDecimal size;\n    private BigDecimal price;\n    private Boolean post_only;\n\n    public NewLimitOrderSingle() {}\n\n    public NewLimitOrderSingle(BigDecimal size, BigDecimal price, Boolean post_only, String product_id) {\n        this.size = size;\n        this.price = price;\n        this.post_only = post_only;\n        super.setProduct_id(product_id);\n    }\n\n    public NewLimitOrderSingle(BigDecimal size, BigDecimal price, Boolean post_only,\n                               String clientOid,\n                               String type,\n                               String side,\n                               String product_id,\n                               String stp,\n                               String funds) {\n        this.size = size;\n        this.price = price;\n        this.post_only = post_only;\n        setClient_oid(clientOid);\n        setType(type);\n        setSide(side);\n        setProduct_id(product_id);\n        setStp(stp);\n        setFunds(funds);\n    }\n\n    public Boolean getPost_only() {\n        return post_only;\n    }\n\n    public void setPost_only(Boolean post_only) {\n        this.post_only = post_only;\n    }\n\n    public BigDecimal getPrice() {\n        return price.setScale(8, RoundingMode.HALF_UP);\n    }\n\n    public void setPrice(BigDecimal price) {\n        this.price = price;\n    }\n\n    public BigDecimal getSize() {\n        return size.setScale(8, RoundingMode.HALF_UP);\n    }\n\n    public void setSize(BigDecimal size) {\n        this.size = size;\n    }\n\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/NewMarketOrderSingle.java",
    "content": "package com.coinbase.exchange.model;\n\nimport java.math.BigDecimal;\n\n/**\n * Created by irufus on 7/31/15.\n */\npublic class NewMarketOrderSingle extends NewOrderSingle {\n\n    private BigDecimal size; //optional: Desired amount in BTC\n\n    public NewMarketOrderSingle(BigDecimal size) {\n        this.size = size;\n    }\n\n    public NewMarketOrderSingle(){\n        super.setType(\"market\");\n    }\n\n    public BigDecimal getSize() {\n        return size;\n    }\n\n    public void setSize(BigDecimal size) {\n        this.size = size;\n    }\n\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/NewOrderSingle.java",
    "content": "package com.coinbase.exchange.model;\n\n/**\n *\n * <pre>\n * {\n *     \"id\": \"d0c5340b-6d6c-49d9-b567-48c4bfca13d2\",\n *     \"price\": \"0.10000000\",\n *     \"size\": \"0.01000000\",\n *     \"product_id\": \"BTC-USD\",\n *     \"side\": \"buy\",\n *     \"stp\": \"dc\",\n *     \"type\": \"limit\",\n *     \"time_in_force\": \"GTC\",\n *     \"post_only\": false,\n *     \"created_at\": \"2016-12-08T20:02:28.53864Z\",\n *     \"fill_fees\": \"0.0000000000000000\",\n *     \"filled_size\": \"0.00000000\",\n *     \"executed_value\": \"0.0000000000000000\",\n *     \"status\": \"pending\",\n *     \"settled\": false\n * }\n * </pre>\n */\npublic abstract class NewOrderSingle {\n\n    private String client_oid; //optional\n    private String type; //default is limit, other types are market and stop\n    private String side;\n    private String product_id;\n    private String stp; //optional: values are dc, co , cn , cb\n    private String funds;\n\n    public NewOrderSingle() {\n    }\n\n    public String getStp() {\n        return stp;\n    }\n\n    public void setStp(String stp) {\n        this.stp = stp;\n    }\n\n    public String getProduct_id() {\n        return product_id;\n    }\n\n    public void setProduct_id(String product_id) {\n        this.product_id = product_id;\n    }\n\n    public String getSide() {\n        return side;\n    }\n\n    public void setSide(String side) {\n        this.side = side;\n    }\n\n    public String getClient_oid() {\n        return client_oid;\n    }\n\n    public void setClient_oid(String client_oid) {\n        this.client_oid = client_oid;\n    }\n\n    public String getFunds() {\n        return funds;\n    }\n\n    public void setFunds(String funds) {\n        this.funds = funds;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/PaymentRequest.java",
    "content": "package com.coinbase.exchange.model;\n\nimport java.math.BigDecimal;\n\n/**\n * Created by robevansuk on 15/02/2017.\n */\npublic class PaymentRequest extends MonetaryRequest {\n\n    private String payment_method_id;\n\n    public PaymentRequest(BigDecimal amount, String currency, String payment_method_id) {\n        super(amount, currency);\n        this.payment_method_id = payment_method_id;\n    }\n\n    public String getPayment_method_id() {\n        return payment_method_id;\n    }\n    public void setPayment_method_id(String payment_method_id) {\n        this.payment_method_id = payment_method_id;\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/PaymentResponse.java",
    "content": "package com.coinbase.exchange.model;\n\nimport java.math.BigDecimal;\n\n/**\n * Created by robevansuk on 15/02/2017.\n */\npublic class PaymentResponse {\n\n    String id;\n    BigDecimal amount;\n    String currency;\n    String payout_at;\n\n    public PaymentResponse() {}\n\n    public PaymentResponse(String id, BigDecimal amount, String currency, String payout_at) {\n        this.id = id;\n        this.amount = amount;\n        this.currency = currency;\n        this.payout_at = payout_at;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public BigDecimal getAmount() {\n        return amount;\n    }\n\n    public void setAmount(BigDecimal amount) {\n        this.amount = amount;\n    }\n\n    public String getCurrency() {\n        return currency;\n    }\n\n    public void setCurrency(String currency) {\n        this.currency = currency;\n    }\n\n    public String getPayout_at() {\n        return payout_at;\n    }\n\n    public void setPayout_at(String payout_at) {\n        this.payout_at = payout_at;\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/Product.java",
    "content": "package com.coinbase.exchange.model;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\n\nimport java.math.BigDecimal;\n\n/**\n * <pre>\n *     {\n *       \"id\": \"BTC-USD\",\n *       \"base_currency\": \"BTC\",\n *       \"quote_currency\": \"USD\",\n *       \"base_min_size\": \"0.001\",\n *       \"base_max_size\": \"280\",\n *       \"base_increment\": \"0.00000001\",\n *       \"quote_increment\": \"0.01\",\n *       \"display_name\": \"BTC/USD\",\n *       \"status\": \"online\",\n *       \"margin_enabled\": false,\n *       \"status_message\": \"\",\n *       \"min_market_funds\": \"5\",\n *       \"max_market_funds\": \"1000000\",\n *       \"post_only\": false,\n *       \"limit_only\": false,\n *       \"cancel_only\": false,\n *       \"type\": \"spot\"\n *     }\n * </pre>\n */\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic class Product {\n    private String id;\n    private String base_currency;\n    private String quote_currency;\n    private Double base_min_size;\n    private Double base_max_size;\n    private Double quote_increment;\n    private double base_increment;\n    private String display_name;\n    private String status;\n    private Boolean margin_enabled;\n    private String status_message;\n    private BigDecimal min_market_funds;\n    private Integer max_market_funds;\n    private Boolean post_only;\n    private Boolean limit_only;\n    private Boolean cancel_only;\n    private String type;\n\n    public Double getQuote_increment() {\n        return quote_increment;\n    }\n\n    public void setQuote_increment(Double quote_increment) {\n        this.quote_increment = quote_increment;\n    }\n\n    public Double getBase_max_size() {\n        return base_max_size;\n    }\n\n    public void setBase_max_size(Double base_max_size) {\n        this.base_max_size = base_max_size;\n    }\n\n    public Double getBase_min_size() {\n        return base_min_size;\n    }\n\n    public void setBase_min_size(Double base_min_size) {\n        this.base_min_size = base_min_size;\n    }\n\n    public String getQuote_currency() {\n        return quote_currency;\n    }\n\n    public void setQuote_currency(String quote_currency) {\n        this.quote_currency = quote_currency;\n    }\n\n    public String getBase_currency() {\n        return base_currency;\n    }\n\n    public void setBase_currency(String base_currency) {\n        this.base_currency = base_currency;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public void setBase_increment(double base_increment) {\n        this.base_increment = base_increment;\n    }\n\n    public double getBase_increment() {\n        return base_increment;\n    }\n\n    public void setDisplay_name(String display_name) {\n        this.display_name = display_name;\n    }\n\n    public String getDisplay_name() {\n        return display_name;\n    }\n\n    public String getStatus() {\n        return status;\n    }\n\n    public void setStatus(String status) {\n        this.status = status;\n    }\n\n    public Boolean getMargin_enabled() {\n        return margin_enabled;\n    }\n\n    public void setMargin_enabled(Boolean margin_enabled) {\n        this.margin_enabled = margin_enabled;\n    }\n\n    public String getStatus_message() {\n        return status_message;\n    }\n\n    public void setStatus_message(String status_message) {\n        this.status_message = status_message;\n    }\n\n    public BigDecimal getMin_market_funds() {\n        return min_market_funds;\n    }\n\n    public void setMin_market_funds(BigDecimal min_market_funds) {\n        this.min_market_funds = min_market_funds;\n    }\n\n    public Integer getMax_market_funds() {\n        return max_market_funds;\n    }\n\n    public void setMax_market_funds(Integer max_market_funds) {\n        this.max_market_funds = max_market_funds;\n    }\n\n    public Boolean getPost_only() {\n        return post_only;\n    }\n\n    public void setPost_only(Boolean post_only) {\n        this.post_only = post_only;\n    }\n\n    public Boolean getLimit_only() {\n        return limit_only;\n    }\n\n    public void setLimit_only(Boolean limit_only) {\n        this.limit_only = limit_only;\n    }\n\n    public Boolean getCancel_only() {\n        return cancel_only;\n    }\n\n    public void setCancel_only(Boolean cancel_only) {\n        this.cancel_only = cancel_only;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n}\n"
  },
  {
    "path": "model/src/main/java/com/coinbase/exchange/model/ProductOrderBook.java",
    "content": "package com.coinbase.exchange.model;\n\nimport java.util.List;\n\n/**\n * Created by irufus on 8/3/15.\n */\npublic class ProductOrderBook {\n\n    private Integer sequence;\n    private List<List<String>> bids;\n    private List<List<String>> asks;\n\n    public List<List<String>> getAsks() {\n        return asks;\n    }\n\n    public void setAsks(List<List<String>> asks) {\n        this.asks = asks;\n    }\n\n    public List<List<String>> getBids() {\n        return bids;\n    }\n\n    public void setBids(List<List<String>> bids) {\n        this.bids = bids;\n    }\n\n    public Integer getSequence() {\n        return sequence;\n    }\n\n    public void setSequence(Integer sequence) {\n        this.sequence = sequence;\n    }\n}\n"
  },
  {
    "path": "security/build.gradle",
    "content": "plugins {\n    id 'java'\n}\n\ngroup 'com.coinbase.exchange'\nversion '0.11.0'\n\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    testCompile group: 'junit', name: 'junit', version: '4.12'\n}\n"
  },
  {
    "path": "security/src/main/java/com/coinbase/exchange/security/Signature.java",
    "content": "package com.coinbase.exchange.security;\n\nimport com.coinbase.exchange.security.constants.ExchangeConstants;\n\nimport javax.crypto.Mac;\nimport javax.crypto.spec.SecretKeySpec;\nimport javax.management.RuntimeErrorException;\nimport java.security.InvalidKeyException;\nimport java.util.Base64;\n\n/**\n * Created by robevansuk on 17/03/2017.\n */\npublic class Signature {\n\n    private final String secretKey;\n\n    public Signature(final String secretKey) {\n        this.secretKey = secretKey;\n    }\n\n    /**\n     * The CB-ACCESS-SIGN header is generated by creating a sha256 HMAC using\n     * the base64-decoded secret key on the prehash string for:\n     * timestamp + method + requestPath + body (where + represents string concatenation)\n     * and base64-encode the output.\n     * The timestamp value is the same as the CB-ACCESS-TIMESTAMP header.\n     * @param requestPath\n     * @param method\n     * @param body\n     * @param timestamp\n     * @return\n     */\n    public String generate(String requestPath, String method, String body, String timestamp) {\n        try {\n            String prehash = timestamp + method.toUpperCase() + requestPath + body;\n            byte[] secretDecoded = Base64.getDecoder().decode(secretKey);\n            SecretKeySpec keyspec = new SecretKeySpec(secretDecoded, ExchangeConstants.SHARED_MAC.getAlgorithm());\n            Mac sha256 = (Mac) ExchangeConstants.SHARED_MAC.clone();\n            sha256.init(keyspec);\n            return Base64.getEncoder().encodeToString(sha256.doFinal(prehash.getBytes()));\n        } catch (CloneNotSupportedException | InvalidKeyException e) {\n            e.printStackTrace();\n            throw new RuntimeErrorException(new Error(\"Cannot set up authentication headers.\"));\n        }\n    }\n}\n"
  },
  {
    "path": "security/src/main/java/com/coinbase/exchange/security/constants/ExchangeConstants.java",
    "content": "package com.coinbase.exchange.security.constants;\n\nimport javax.crypto.Mac;\nimport java.security.NoSuchAlgorithmException;\n\n/**\n * Created by robevansuk on 25/01/2017.\n */\npublic class ExchangeConstants {\n\n    public static Mac SHARED_MAC;\n\n    static {\n        try {\n            SHARED_MAC = Mac.getInstance(\"HmacSHA256\");\n        } catch (NoSuchAlgorithmException nsaEx) {\n            nsaEx.printStackTrace();\n        }\n    }\n}\n"
  },
  {
    "path": "settings.gradle",
    "content": "rootProject.name = 'coinbase-pro-java'\n\ninclude 'api'\ninclude 'security'\ninclude 'websocketfeed'\ninclude 'model'\ninclude 'security'\n\n"
  },
  {
    "path": "websocketfeed/build.gradle",
    "content": "dependencies {\n    compile project(':model')\n    compile project(':security')\n    implementation 'org.springframework.boot:spring-boot-starter-web'\n    implementation 'com.fasterxml.jackson.core:jackson-core'\n    implementation 'com.fasterxml.jackson.core:jackson-databind'\n    implementation 'com.fasterxml.jackson.core:jackson-annotations'\n    implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8'\n    implementation 'org.slf4j:slf4j-api'\n    implementation 'ch.qos.logback:logback-classic'\n    implementation 'ch.qos.logback:logback-core'\n\n    testImplementation 'org.junit.jupiter:junit-jupiter-engine'\n    testImplementation 'org.junit.jupiter:junit-jupiter-api'\n    testImplementation 'org.junit.jupiter:junit-jupiter-params'\n    testImplementation 'org.junit.jupiter:junit-jupiter-engine'\n    testImplementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/ActivateOrderBookMessage.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport java.math.BigDecimal;\nimport java.time.Instant;\n\n/**\n * An activate message is sent when a stop order is placed.\n * When the stop is triggered the order will be placed and go through the order lifecycle.\n * Example:\n * <pre>\n * {\n *   \"type\": \"activate\",\n *   \"product_id\": \"test-product\",\n *   \"timestamp\": \"1483736448.299000\",\n *   \"user_id\": \"12\",\n *   \"profile_id\": \"30000727-d308-cf50-7b1c-c06deb1934fc\",\n *   \"order_id\": \"7b52009b-64fd-0a2a-49e6-d8a939753077\",\n *   \"stop_type\": \"entry\",\n *   \"side\": \"buy\",\n *   \"stop_price\": \"80\",\n *   \"size\": \"2\",\n *   \"funds\": \"50\",\n *   \"private\": true\n * }\n * </pre>\n */\npublic class ActivateOrderBookMessage extends OrderBookMessage {\n    private String stop_type;\n    private BigDecimal stop_price;\n    private Instant timestamp;\n\n    public ActivateOrderBookMessage() {\n        setType(\"activate\");\n    }\n\n    public ActivateOrderBookMessage(String stop_type, BigDecimal stop_price, Instant timestamp, boolean privateFlag) {\n        this();\n        this.stop_type = stop_type;\n        this.stop_price = stop_price;\n        this.timestamp = timestamp;\n        this.privateFlag = privateFlag;\n    }\n\n    @JsonProperty(\"private\")\n    private boolean privateFlag;\n\n    public String getStop_type() {\n        return stop_type;\n    }\n\n    public void setStop_type(String stop_type) {\n        this.stop_type = stop_type;\n    }\n\n    public BigDecimal getStop_price() {\n        return stop_price;\n    }\n\n    public void setStop_price(BigDecimal stop_price) {\n        this.stop_price = stop_price;\n    }\n\n    public Instant getTimestamp() {\n        return timestamp;\n    }\n\n    public void setTimestamp(Instant timestamp) {\n        this.timestamp = timestamp;\n    }\n\n    public boolean isPrivateFlag() {\n        return privateFlag;\n    }\n\n    public void setPrivateFlag(boolean privateFlag) {\n        this.privateFlag = privateFlag;\n    }\n\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/ChangedOrderBookMessage.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\n/**\n * An order has changed. This is the result of self-trade prevention\n * adjusting the order size or available funds. Orders can only\n * decrease in size or funds. change messages are sent anytime an\n * order changes in size; this includes resting orders (open) as\n * well as received but not yet open. change messages are also\n * sent when a new market order goes through self trade prevention\n * and the funds for the market order have changed.\n * <pre>\n * {\n *     \"type\": \"change\",\n *     \"time\": \"2014-11-07T08:19:27.028459Z\",\n *     \"sequence\": 80,\n *     \"order_id\": \"ac928c66-ca53-498f-9c13-a110027a60e8\",\n *     \"product_id\": \"BTC-USD\",\n *     \"new_size\": \"5.23512\",\n *     \"old_size\": \"12.234412\",\n *     \"price\": \"400.23\",\n *     \"side\": \"sell\"\n * }\n * </pre>\n * change messages for received but not yet open orders can be\n * ignored when building a real-time order book. The side field\n * of a change message and price can be used as indicators for whether\n * the change message is relevant if building from a level 2 book.\n *\n * Any change message where the price is null indicates that the change\n * message is for a market order. Change messages for limit orders will\n * always have a price specified.\n * <pre>\n * {\n *     \"type\": \"change\",\n *     \"time\": \"2014-11-07T08:19:27.028459Z\",\n *     \"sequence\": 80,\n *     \"order_id\": \"ac928c66-ca53-498f-9c13-a110027a60e8\",\n *     \"product_id\": \"BTC-USD\",\n *     \"new_funds\": \"5.23512\",\n *     \"old_funds\": \"12.234412\",\n *     \"price\": \"400.23\",\n *     \"side\": \"sell\"\n * }\n * </pre>\n */\npublic class ChangedOrderBookMessage extends OrderBookMessage {\n\n    public ChangedOrderBookMessage() {\n        setType(\"change\");\n    }\n\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/Channel.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport static com.coinbase.exchange.websocketfeed.ChannelName.full;\nimport static com.coinbase.exchange.websocketfeed.ChannelName.heartbeat;\nimport static com.coinbase.exchange.websocketfeed.ChannelName.level2;\nimport static com.coinbase.exchange.websocketfeed.ChannelName.matches;\nimport static com.coinbase.exchange.websocketfeed.ChannelName.status;\nimport static com.coinbase.exchange.websocketfeed.ChannelName.ticker;\nimport static com.coinbase.exchange.websocketfeed.ChannelName.user;\n\npublic class Channel {\n    public static final Channel CHANNEL_HEARTBEAT = new Channel(heartbeat, null);\n    public static final Channel CHANNEL_STATUS = new Channel(status, null);\n    public static final Channel CHANNEL_TICKER = new Channel(ticker, null);\n    public static final Channel CHANNEL_LEVEL2 = new Channel(level2, null);\n    public static final Channel CHANNEL_USER = new Channel(user, null);\n    public static final Channel CHANNEL_MATCHES = new Channel(matches, null);\n    public static final Channel CHANNEL_FULL = new Channel(full, null);\n\n    private ChannelName name;\n\n    @JsonProperty(\"product_ids\")\n    @JsonInclude(JsonInclude.Include.NON_NULL)\n    private String[] product_ids;\n\n    public Channel() {\n    }\n\n    public Channel(ChannelName name, String[] product_ids) {\n        this.name = name;\n        this.product_ids = product_ids;\n    }\n\n    public ChannelName getName() {\n        return name;\n    }\n\n    public void setName(ChannelName name) {\n        this.name = name;\n    }\n\n    public String[] getProduct_ids() {\n        return product_ids;\n    }\n\n    public void setProduct_ids(String[] product_ids) {\n        this.product_ids = product_ids;\n    }\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/ChannelName.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\npublic enum ChannelName {\n    heartbeat, status, ticker, level2, user, matches, full\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/DoneOrderBookMessage.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\n/**\n * <pre>\n *  {\n *    \"type\": \"done\",\n *    \"time\": \"2014-11-07T08:19:27.028459Z\",\n *    \"product_id\": \"BTC-USD\",\n *    \"sequence\": 10,\n *    \"price\": \"200.2\",\n *    \"order_id\": \"d50ec984-77a8-460a-b958-66f114b0de9b\",\n *    \"reason\": \"filled\", // canceled\n *    \"side\": \"sell\",\n *    \"remaining_size\": \"0.2\"\n *  }\n * </pre>\n */\npublic class DoneOrderBookMessage extends OrderBookMessage {\n\n    public DoneOrderBookMessage() {\n        setType(\"done\");\n    }\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/ErrorOrderBookMessage.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\n/**\n * If you send a message that is not recognized or an error\n * occurs, the error message will be sent and you will be\n * disconnected.\n * <pre>\n * {\n *     \"type\": \"error\",\n *     \"message\": \"error message\"\n * }\n * </pre>\n */\npublic class ErrorOrderBookMessage extends FeedMessage {\n\n    private String message;\n\n    public ErrorOrderBookMessage() {\n        setType(\"error\");\n    }\n\n    public ErrorOrderBookMessage(String message) {\n        this();\n        this.message = message;\n    }\n\n    //    @JsonCreator\n//    public ErrorOrderBookMessage(String type, Long sequence, Instant time, String product_id, String message) {\n//        super(type, sequence, time, product_id);\n//        this.message = message;\n//    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public void setMessage(String message) {\n        this.message = message;\n    }\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/FeedMessage.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonSubTypes;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport java.time.Instant;\n\n@JsonIgnoreProperties(ignoreUnknown = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = \"type\")\n@JsonSubTypes({\n        @JsonSubTypes.Type(value = ErrorOrderBookMessage.class, name = \"error\"),\n        @JsonSubTypes.Type(value = SubscriptionsMessage.class, name = \"subscriptions\"),\n        @JsonSubTypes.Type(value = HeartBeat.class, name = \"heartbeat\"),\n        @JsonSubTypes.Type(value = ChangedOrderBookMessage.class, name = \"change\"),\n        @JsonSubTypes.Type(value = DoneOrderBookMessage.class, name = \"done\"),\n        @JsonSubTypes.Type(value = MatchedOrderBookMessage.class, name = \"match\"),\n        @JsonSubTypes.Type(value = MatchedOrderBookMessage.class, name = \"last_match\"),\n        @JsonSubTypes.Type(value = OpenedOrderBookMessage.class, name = \"open\"),\n        @JsonSubTypes.Type(value = ReceivedOrderBookMessage.class, name = \"received\"),\n        @JsonSubTypes.Type(value = TickerMessage.class, name = \"ticker\"),\n        @JsonSubTypes.Type(value = ActivateOrderBookMessage.class, name = \"activate\"),\n        @JsonSubTypes.Type(value = StatusMessage.class, name = \"status\"),\n        @JsonSubTypes.Type(value = SnapshotMessage.class, name = \"snapshot\"),\n        @JsonSubTypes.Type(value = L2UpdateMessage.class, name = \"l2update\"),\n})\npublic abstract class FeedMessage {\n\n    private String type;  // \"received\" | \"open\" | \"done\" | \"match\" | \"change\" | \"activate\"\n    private Long sequence;\n    private Instant time;\n    private String product_id;\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public Long getSequence() {\n        return sequence;\n    }\n\n    public void setSequence(Long sequence) {\n        this.sequence = sequence;\n    }\n\n    public Instant getTime() {\n        return time;\n    }\n\n    public void setTime(Instant time) {\n        this.time = time;\n    }\n\n    public String getProduct_id() {\n        return product_id;\n    }\n\n    public void setProduct_id(String product_id) {\n        this.product_id = product_id;\n    }\n\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/HeartBeat.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\n/**\n * A message sent once a second when heartbeat is turned on.\n * <pre>\n * {\n *     \"type\": \"heartbeat\",                   // inherited\n *     \"sequence\": 90,                        // inherited\n *     \"last_trade_id\": 20,\n *     \"product_id\": \"BTC-USD\",               // inherited\n *     \"time\": \"2014-11-07T08:19:28.464459Z\"  // inherited\n * }\n * </pre>\n */\npublic class HeartBeat extends FeedMessage {\n\n    private Long last_trade_id;\n\n    public HeartBeat() {\n        setType(\"heartbeat\");\n    }\n\n    public HeartBeat(Long last_trade_id) {\n        this();\n        this.last_trade_id = last_trade_id;\n    }\n\n    public Long getLast_trade_id() {\n        return last_trade_id;\n    }\n\n    public void setLast_trade_id(Long last_trade_id) {\n        this.last_trade_id = last_trade_id;\n    }\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/L2UpdateMessage.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\n/**\n * Subsequent updates will have the type l2update.\n * The changes property of l2updates is an array with [side, price, size] tuples.\n * 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.\n * Example:\n * <pre>\n * {\n *   \"type\": \"l2update\",\n *   \"product_id\": \"BTC-GBP\",\n *   \"changes\": [\n *     [\n *       \"buy\",\n *       \"5454.12\",\n *       \"0.00000000\"\n *     ]\n *   ],\n *   \"time\": \"2020-04-10T15:28:07.393966Z\"\n * }\n * </pre>\n */\npublic class L2UpdateMessage extends FeedMessage {\n\n    private String[][] changes;\n\n    public L2UpdateMessage() {\n        setType(\"l2update\");\n    }\n\n    public L2UpdateMessage(String[][] changes) {\n        this();\n        this.changes = changes;\n    }\n\n    public String[][] getChanges() {\n        return changes;\n    }\n\n    public void setChanges(String[][] changes) {\n        this.changes = changes;\n    }\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/MatchedOrderBookMessage.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\n/**\n * A trade occurred between two orders. The aggressor or taker order is\n * the one executing immediately after being received and the maker order\n * is a resting order on the book. The side field indicates the maker\n * order side. If the side is sell this indicates the maker was a sell\n * order and the match is considered an up-tick. A buy side match is a\n * down-tick.\n * <pre>\n *  {\n *    \"type\": \"match\",\n *    \"trade_id\": 10,\n *    \"sequence\": 50,\n *    \"maker_order_id\": \"ac928c66-ca53-498f-9c13-a110027a60e8\",\n *    \"taker_order_id\": \"132fb6ae-456b-4654-b4e0-d681ac05cea1\",\n *    \"time\": \"2014-11-07T08:19:27.028459Z\",\n *    \"product_id\": \"BTC-USD\",\n *    \"size\": \"5.23512\",\n *    \"price\": \"400.23\",\n *    \"side\": \"sell\"\n *  }\n * </pre>\n * If authenticated, and you were the taker, the message would also have\n * the following fields:\n * <pre>\n *  taker_user_id: \"5844eceecf7e803e259d0365\",\n *  user_id: \"5844eceecf7e803e259d0365\",\n *  taker_profile_id: \"765d1549-9660-4be2-97d4-fa2d65fa3352\",\n *  profile_id: \"765d1549-9660-4be2-97d4-fa2d65fa3352\"\n * </pre>\n */\npublic class MatchedOrderBookMessage extends OrderBookMessage {\n\n    public MatchedOrderBookMessage() {\n        setType(\"match\");\n    }\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/OpenedOrderBookMessage.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\n/**\n * <pre>\n *  {\n *    \"type\": \"open\",\n *    \"time\": \"2014-11-07T08:19:27.028459Z\",\n *    \"product_id\": \"BTC-USD\",\n *    \"sequence\": 10,\n *    \"order_id\": \"d50ec984-77a8-460a-b958-66f114b0de9b\",\n *    \"price\": \"200.2\",\n *    \"remaining_size\": \"1.00\",\n *    \"side\": \"sell\"\n *  }\n * </pre>\n */\npublic class OpenedOrderBookMessage extends OrderBookMessage {\n\n    public OpenedOrderBookMessage() {\n        setType(\"open\");\n    }\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/OrderBookMessage.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.time.Instant;\n\n/**\n * Generic message that will be passed as an argument to other message types\n * so the relevant parts can be determined and the messages typed.\n * Created by robevansuk on 15/03/2017.\n */\npublic class OrderBookMessage extends FeedMessage implements Comparable<OrderBookMessage> {\n\n    String trade_id;\n    String side;\n    String order_id;\n    String order_type;\n\n    BigDecimal funds;\n\n    BigDecimal size;\n    BigDecimal price;\n\n    BigDecimal new_size;\n    BigDecimal old_size;\n    BigDecimal new_funds;\n    BigDecimal old_funds;\n\n    String reason;\n    BigDecimal remaining_size;\n\n    String maker_order_id;\n    String taker_order_id;\n    String taker_user_id;\n    String user_id;\n    String taker_profile_id;\n    String profile_id;\n\n    String last_trade_id;\n\n    String client_oid;\n    String stp;\n\n    String message;\n    String open_24h;\n    String volume_24h;\n    String low_24h;\n    String high_24h;\n    String volume_30d;\n    String best_bid;\n    String best_ask;\n    String last_size;\n    Channel[] channels;\n\n    public OrderBookMessage() {\n\n    }\n\n    public OrderBookMessage(String type, String time, String product_id,\n                            String trade_id, Long sequence, String side,\n                            String order_id, String order_type, BigDecimal funds,\n                            BigDecimal size, BigDecimal price, BigDecimal new_size,\n                            BigDecimal old_size, BigDecimal new_funds,\n                            BigDecimal old_funds, String reason,\n                            BigDecimal remaining_size, String maker_order_id,\n                            String taker_order_id, String taker_user_id, String user_id,\n                            String taker_profile_id, String profile_id, String last_trade_id,\n                            String client_oid, String stp,\n                            String message,\n                            String open_24h, String volume_24h, String low_24h,\n                            String high_24h, String volume_30d, String best_bid,\n                            String best_ask, String last_size, Channel[] channels) {\n        setType(type);\n        setTime(Instant.parse(time));\n        setProduct_id(product_id);\n        setSequence(sequence);\n        this.trade_id = trade_id;\n        this.side = side;\n        this.order_id = order_id;\n        this.order_type = order_type;\n        this.funds = funds;\n        this.size = size;\n        this.price = price;\n        this.new_size = new_size;\n        this.old_size = old_size;\n        this.new_funds = new_funds;\n        this.old_funds = old_funds;\n        this.reason = reason;\n        this.remaining_size = remaining_size;\n        this.maker_order_id = maker_order_id;\n        this.taker_order_id = taker_order_id;\n        this.taker_user_id = taker_user_id;\n        this.user_id = user_id;\n        this.taker_profile_id = taker_profile_id;\n        this.profile_id = profile_id;\n        this.last_trade_id = last_trade_id;\n        this.client_oid = client_oid;\n        this.stp = stp;\n        this.message = message;\n        this.open_24h = open_24h;\n        this.volume_24h = volume_24h;\n        this.low_24h = low_24h;\n        this.high_24h = high_24h;\n        this.volume_30d = volume_30d;\n        this.best_bid = best_bid;\n        this.best_ask = best_ask;\n        this.last_size = last_size;\n        this.channels = channels;\n    }\n\n    public String getClient_oid() {\n        return client_oid;\n    }\n\n    public void setClient_oid(String client_oid) {\n        this.client_oid = client_oid;\n    }\n\n    public String getStp() {\n        return stp;\n    }\n\n    public void setStp(String stp) {\n        this.stp = stp;\n    }\n\n    public String getTrade_id() {\n        return trade_id;\n    }\n\n    public void setTrade_id(String trade_id) {\n        this.trade_id = trade_id;\n    }\n\n    public String getSide() {\n        return side;\n    }\n\n    public void setSide(String side) {\n        this.side = side;\n    }\n\n    public String getOrder_id() {\n        return order_id;\n    }\n\n    public void setOrder_id(String order_id) {\n        this.order_id = order_id;\n    }\n\n    public String getOrder_type() {\n        return order_type;\n    }\n\n    public void setOrder_type(String order_type) {\n        this.order_type = order_type;\n    }\n\n    public BigDecimal getFunds() {\n        return funds;\n    }\n\n    public void setFunds(BigDecimal funds) {\n        this.funds = funds;\n    }\n\n    public BigDecimal getSize() {\n        return size;\n    }\n\n    public void setSize(BigDecimal size) {\n        this.size = size;\n    }\n\n    public BigDecimal getPrice() {\n        return price.setScale(8, RoundingMode.HALF_UP);\n    }\n\n    public void setPrice(BigDecimal price) {\n        this.price = price;\n    }\n\n    public BigDecimal getNew_size() {\n        return new_size;\n    }\n\n    public void setNew_size(BigDecimal new_size) {\n        this.new_size = new_size;\n    }\n\n    public BigDecimal getOld_size() {\n        return old_size;\n    }\n\n    public void setOld_size(BigDecimal old_size) {\n        this.old_size = old_size;\n    }\n\n    public BigDecimal getNew_funds() {\n        return new_funds;\n    }\n\n    public void setNew_funds(BigDecimal new_funds) {\n        this.new_funds = new_funds;\n    }\n\n    public BigDecimal getOld_funds() {\n        return old_funds;\n    }\n\n    public void setOld_funds(BigDecimal old_funds) {\n        this.old_funds = old_funds;\n    }\n\n    public String getReason() {\n        return reason;\n    }\n\n    public void setReason(String reason) {\n        this.reason = reason;\n    }\n\n    public BigDecimal getRemaining_size() {\n        return remaining_size;\n    }\n\n    public void setRemaining_size(BigDecimal remaining_size) {\n        this.remaining_size = remaining_size;\n    }\n\n    public String getMaker_order_id() {\n        return maker_order_id;\n    }\n\n    public void setMaker_order_id(String maker_order_id) {\n        this.maker_order_id = maker_order_id;\n    }\n\n    public String getTaker_order_id() {\n        return taker_order_id;\n    }\n\n    public void setTaker_order_id(String taker_order_id) {\n        this.taker_order_id = taker_order_id;\n    }\n\n    public String getTaker_user_id() {\n        return taker_user_id;\n    }\n\n    public void setTaker_user_id(String taker_user_id) {\n        this.taker_user_id = taker_user_id;\n    }\n\n    public String getUser_id() {\n        return user_id;\n    }\n\n    public void setUser_id(String user_id) {\n        this.user_id = user_id;\n    }\n\n    public String getTaker_profile_id() {\n        return taker_profile_id;\n    }\n\n    public void setTaker_profile_id(String taker_profile_id) {\n        this.taker_profile_id = taker_profile_id;\n    }\n\n    public String getProfile_id() {\n        return profile_id;\n    }\n\n    public void setProfile_id(String profile_id) {\n        this.profile_id = profile_id;\n    }\n    public String getLast_trade_id() {\n        return last_trade_id;\n    }\n\n    public void setLast_trade_id(String last_trade_id) {\n        this.last_trade_id = last_trade_id;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public void setMessage(String message) {\n        this.message = message;\n    }\n\n    public String getOpen_24h() {\n        return open_24h;\n    }\n\n    public void setOpen_24h(String open_24h) {\n        this.open_24h = open_24h;\n    }\n\n    public String getVolume_24h() {\n        return volume_24h;\n    }\n\n    public void setVolume_24h(String volume_24h) {\n        this.volume_24h = volume_24h;\n    }\n\n    public String getLow_24h() {\n        return low_24h;\n    }\n\n    public void setLow_24h(String low_24h) {\n        this.low_24h = low_24h;\n    }\n\n    public String getHigh_24h() {\n        return high_24h;\n    }\n\n    public void setHigh_24h(String high_24h) {\n        this.high_24h = high_24h;\n    }\n\n    public String getVolume_30d() {\n        return volume_30d;\n    }\n\n    public void setVolume_30d(String volume_30d) {\n        this.volume_30d = volume_30d;\n    }\n\n    public String getBest_bid() {\n        return best_bid;\n    }\n\n    public void setBest_bid(String best_bid) {\n        this.best_bid = best_bid;\n    }\n\n    public String getBest_ask() {\n        return best_ask;\n    }\n\n    public void setBest_ask(String best_ask) {\n        this.best_ask = best_ask;\n    }\n\n    public String getLast_size() {\n        return last_size;\n    }\n\n    public void setLast_size(String last_size) {\n        this.last_size = last_size;\n    }\n\n    public Channel[] getChannels() {\n        return channels;\n    }\n\n    public void setChannels(Channel[] channels) {\n        this.channels = channels;\n    }\n\n    @Override\n    public int compareTo(OrderBookMessage other) {\n        return this.getSequence().compareTo(other.getSequence());\n    }\n\n    @Override\n    public String toString() {\n        return \"OrderBookMessage{\" +\n                \"side='\" + side + '\\'' +\n                \", type='\" + getType() + '\\'' +\n                \", size=\" + size +\n                \", price=\" + getPrice() +\n                \", remaining_size=\" + remaining_size +\n                \", sequence=\" + getSequence() +\n                '}';\n    }\n\n    public static class OrderBookMessageBuilder {\n        private final OrderBookMessage message = new OrderBookMessage();\n\n        public OrderBookMessageBuilder setType(String type) {\n            message.setType(type);\n            return this;\n        }\n        public OrderBookMessageBuilder setSide(String side) {\n            message.setSide(side);\n            return this;\n        }\n\n        public OrderBookMessageBuilder setPrice(BigDecimal price) {\n            message.setPrice(price);\n            return this;\n        }\n\n        public OrderBookMessageBuilder setSize(BigDecimal size) {\n            message.setSize(size);\n            return this;\n        }\n\n        public OrderBookMessageBuilder setRemainingSize(BigDecimal remaininSize) {\n            message.setRemaining_size(remaininSize);\n            return this;\n        }\n\n        public OrderBookMessageBuilder setSequence(Long id) {\n            message.setSequence(id);\n            return this;\n        }\n\n        public OrderBookMessage build() {\n            return message;\n        }\n    }\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/ReceivedOrderBookMessage.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\n\n/**\n * A valid order has been received and is now active. This message is\n * emitted for every single valid order as soon as the matching engine\n * receives it whether it fills immediately or not.\n *\n * The received message does not indicate a resting order on the order book.\n * It simply indicates a new incoming order which as been accepted by the\n * matching engine for processing. Received orders may cause match message\n * to follow if they are able to begin being filled (taker behavior).\n * Self-trade prevention may also trigger change messages to follow if the\n * order size needs to be adjusted. Orders which are not fully filled or\n * canceled due to self-trade prevention result in an open message and\n * become resting orders on the order book.\n *\n * Market orders (indicated by the order_type field) may have an optional\n * funds field which indicates how much quote currency will be used to buy\n * or sell. For example, a funds field of 100.00 for the BTC-USD product\n * would indicate a purchase of up to 100.00 USD worth of bitcoin.\n * <pre>\n * {\n *    \"type\": \"received\",\n *    \"time\": \"2014-11-07T08:19:27.028459Z\",\n *    \"product_id\": \"BTC-USD\",\n *    \"sequence\": 10,\n *    \"order_id\": \"d50ec984-77a8-460a-b958-66f114b0de9b\",\n *    \"size\": \"1.34\",\n *    \"price\": \"502.1\",\n *    \"side\": \"buy\",\n *    \"order_type\": \"limit\"\n *  }\n *\n *  {\n *    \"type\": \"received\",\n *    \"time\": \"2014-11-09T08:19:27.028459Z\",\n *    \"product_id\": \"BTC-USD\",\n *    \"sequence\": 12,\n *    \"order_id\": \"dddec984-77a8-460a-b958-66f114b0de9b\",\n *    \"funds\": \"3000.234\",\n *    \"side\": \"buy\",\n *    \"order_type\": \"market\"\n *  }\n * </pre>\n */\npublic class ReceivedOrderBookMessage extends OrderBookMessage {\n\n    public ReceivedOrderBookMessage() {\n        setType(\"received\");\n    }\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/SnapshotMessage.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\n/**\n * A snapshot of the order book\n * Example:\n * <pre>\n * {\n *     \"type\": \"snapshot\",\n *     \"product_id\": \"BTC-USD\",\n *     \"bids\": [[\"10101.10\", \"0.45054140\"]],\n *     \"asks\": [[\"10102.55\", \"0.57753524\"]]\n * }\n * </pre>\n */\npublic class SnapshotMessage extends FeedMessage {\n\n    private String[][] bids;\n    private String[][] asks;\n\n    public SnapshotMessage() {\n        setType(\"snapshot\");\n    }\n\n    public SnapshotMessage(String[][] bids, String[][] asks) {\n        this();\n        this.bids = bids;\n        this.asks = asks;\n    }\n\n    public String[][] getBids() {\n        return bids;\n    }\n\n    public void setBids(String[][] bids) {\n        this.bids = bids;\n    }\n\n    public String[][] getAsks() {\n        return asks;\n    }\n\n    public void setAsks(String[][] asks) {\n        this.asks = asks;\n    }\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/StatusMessage.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.coinbase.exchange.model.Currency;\nimport com.coinbase.exchange.model.Product;\n\n/**\n * The status channel will send all products and currencies on a preset interval.\n * Example:\n * <pre>\n * {\n *     \"type\": \"status\",\n *     \"products\": [\n *         {\n *             \"id\": \"BTC-USD\",\n *             \"base_currency\": \"BTC\",\n *             \"quote_currency\": \"USD\",\n *             \"base_min_size\": \"0.001\",\n *             \"base_max_size\": \"70\",\n *             \"base_increment\": \"0.00000001\",\n *             \"quote_increment\": \"0.01\",\n *             \"display_name\": \"BTC/USD\",\n *             \"status\": \"online\",\n *             \"status_message\": null,\n *             \"min_market_funds\": \"10\",\n *             \"max_market_funds\": \"1000000\",\n *             \"post_only\": false,\n *             \"limit_only\": false,\n *             \"cancel_only\": false\n *         }\n *     ],\n *     \"currencies\": [\n *         {\n *             \"id\": \"USD\",\n *             \"name\": \"United States Dollar\",\n *             \"min_size\": \"0.01000000\",\n *             \"status\": \"online\",\n *             \"status_message\": null,\n *             \"max_precision\": \"0.01\",\n *             \"convertible_to\": [\"USDC\"],\n *             \"details\": {}\n *         },\n *         {\n *             \"id\": \"USDC\",\n *             \"name\": \"USD Coin\",\n *             \"min_size\": \"0.00000100\",\n *             \"status\": \"online\",\n *             \"status_message\": null,\n *             \"max_precision\": \"0.000001\",\n *             \"convertible_to\": [\"USD\"],\n *             \"details\": {}\n *         },\n *         {\n *             \"id\": \"BTC\",\n *             \"name\": \"Bitcoin\",\n *             \"min_size\":\" 0.00000001\",\n *             \"status\": \"online\",\n *             \"status_message\": null,\n *             \"max_precision\": \"0.00000001\",\n *             \"convertible_to\": []\n *         }\n *     ]\n * }\n * </pre>\n */\npublic class StatusMessage extends FeedMessage {\n\n    private Product[] products;\n    private Currency[] currencies;\n\n    public StatusMessage() {\n        setType(\"status\");\n    }\n\n    public StatusMessage(Product[] products, Currency[] currencies) {\n        this();\n        this.products = products;\n        this.currencies = currencies;\n    }\n\n    public Product[] getProducts() {\n        return products;\n    }\n\n    public void setProducts(Product[] products) {\n        this.products = products;\n    }\n\n    public Currency[] getCurrencies() {\n        return currencies;\n    }\n\n    public void setCurrencies(Currency[] currencies) {\n        this.currencies = currencies;\n    }\n}"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/Subscribe.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport static com.coinbase.exchange.websocketfeed.ChannelName.full;\n\n/**\n * To begin receiving feed messages, you must first send a subscribe message\n * indicating which channels and products to receive.\n * Example:\n * Subscribe to ETH-USD and ETH-EUR with the level2, heartbeat and ticker channels,\n * plus receive the ticker entries for ETH-BTC and ETH-USD\n * <pre>\n * {\n *   \"type\": \"subscribe\",\n *   \"product_ids\": [\n *     \"ETH-USD\",\n *     \"ETH-EUR\"\n *   ],\n *   \"channels\": [\n *     \"level2\",\n *     \"heartbeat\",\n *     {\n *       \"name\": \"ticker\",\n *       \"product_ids\": [\n *         \"ETH-BTC\",\n *         \"ETH-USD\"\n *       ]\n *     }\n *   ]\n * }\n * </pre>\n *\n * See docs https://docs.pro.coinbase.com/#subscribe\n */\npublic class Subscribe {\n\n    private final static String SUBSCRIBE_MSG_TYPE = \"subscribe\";\n\n    private String type = SUBSCRIBE_MSG_TYPE;\n    private String[] product_ids;\n    private Channel[] channels;\n\n    // Used for signing the subscribe message to the Websocket feed\n    private String signature;\n    private String passphrase;\n    private String timestamp;\n    private String apiKey;\n\n    public Subscribe() { }\n\n    public Subscribe(String[] product_ids) {\n        this.product_ids = product_ids;\n        this.channels = new Channel[]{new Channel(full, product_ids)};\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public String[] getProduct_ids() {\n        return product_ids;\n    }\n\n    public void setProduct_ids(String[] product_ids) {\n        this.product_ids = product_ids;\n    }\n\n    public Subscribe setSignature(String signature) {\n        this.signature = signature;\n        return this;\n    }\n\n    public Subscribe setPassphrase(String passphrase) {\n        this.passphrase = passphrase;\n        return this;\n    }\n\n    public Subscribe setTimestamp(String timestamp) {\n        this.timestamp = timestamp;\n        return this;\n    }\n\n    public Subscribe setKey(String apiKey) {\n        this.apiKey = apiKey;\n        return this;\n    }\n\n    public void setChannels(Channel[] channels) {\n        this.channels = channels;\n    }\n\n    public Channel[] getChannels() {\n        return channels;\n    }\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/SubscriptionsMessage.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\n/**\n * <pre>\n * {\n *     \"type\": \"subscriptions\",\n *     \"channels\": [\n *         {\n *             \"name\": \"level2\",\n *             \"product_ids\": [\n *                 \"ETH-USD\",\n *                 \"ETH-EUR\"\n *             ],\n *         },\n *         {\n *             \"name\": \"heartbeat\",\n *             \"product_ids\": [\n *                 \"ETH-USD\",\n *                 \"ETH-EUR\"\n *             ],\n *         },\n *         {\n *             \"name\": \"ticker\",\n *             \"product_ids\": [\n *                 \"ETH-USD\",\n *                 \"ETH-EUR\",\n *                 \"ETH-BTC\"\n *             ]\n *         }\n *     ]\n * }\n * </pre>\n */\npublic class SubscriptionsMessage extends FeedMessage {\n\n    private Channel[] channels;\n\n\n\n    public Channel[] getChannels() {\n        return channels;\n    }\n\n    public void setChannels(Channel[] channels) {\n        this.channels = channels;\n    }\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/TickerMessage.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport java.math.BigDecimal;\nimport java.time.Instant;\n\n/**\n * The ticker channel provides real-time price updates every time a match happens\n * <pre>\n * {\n *     \"type\": \"ticker\",\n *     \"trade_id\": 20153558,\n *     \"sequence\": 3262786978,\n *     \"time\": \"2017-09-02T17:05:49.250000Z\",\n *     \"product_id\": \"BTC-USD\",\n *     \"price\": \"4388.01000000\",\n *     \"side\": \"buy\", // Taker side\n *     \"last_size\": \"0.03000000\",\n *     \"best_bid\": \"4388\",\n *     \"best_ask\": \"4388.01\"\n * }\n * </pre>\n */\npublic class TickerMessage extends FeedMessage {\n\n    private Long trade_id;\n    private Long sequence;\n    private Instant time;\n    private String product_id;\n    private BigDecimal price;\n    private String side;\n    private BigDecimal last_size;\n    private BigDecimal best_bid;\n    private BigDecimal best_ask;\n\n    public TickerMessage() {\n        setType(\"ticker\");\n    }\n\n    public TickerMessage(Long trade_id,\n                         Long sequence,\n                         Instant time,\n                         String product_id,\n                         BigDecimal price,\n                         String side,\n                         BigDecimal last_size,\n                         BigDecimal best_bid,\n                         BigDecimal best_ask) {\n        this();\n        this.trade_id = trade_id;\n        this.sequence = sequence;\n        this.time = time;\n        this.product_id = product_id;\n        this.price = price;\n        this.side = side;\n        this.last_size = last_size;\n        this.best_bid = best_bid;\n        this.best_ask = best_ask;\n    }\n\n    public Long getTrade_id() {\n        return trade_id;\n    }\n\n    public void setTrade_id(Long trade_id) {\n        this.trade_id = trade_id;\n    }\n\n    @Override\n    public Long getSequence() {\n        return sequence;\n    }\n\n    @Override\n    public void setSequence(Long sequence) {\n        this.sequence = sequence;\n    }\n\n    @Override\n    public Instant getTime() {\n        return time;\n    }\n\n    @Override\n    public void setTime(Instant time) {\n        this.time = time;\n    }\n\n    @Override\n    public String getProduct_id() {\n        return product_id;\n    }\n\n    @Override\n    public void setProduct_id(String product_id) {\n        this.product_id = product_id;\n    }\n\n    public BigDecimal getPrice() {\n        return price;\n    }\n\n    public void setPrice(BigDecimal price) {\n        this.price = price;\n    }\n\n    public String getSide() {\n        return side;\n    }\n\n    public void setSide(String side) {\n        this.side = side;\n    }\n\n    public BigDecimal getLast_size() {\n        return last_size;\n    }\n\n    public void setLast_size(BigDecimal last_size) {\n        this.last_size = last_size;\n    }\n\n    public BigDecimal getBest_bid() {\n        return best_bid;\n    }\n\n    public void setBest_bid(BigDecimal best_bid) {\n        this.best_bid = best_bid;\n    }\n\n    public BigDecimal getBest_ask() {\n        return best_ask;\n    }\n\n    public void setBest_ask(BigDecimal best_ask) {\n        this.best_ask = best_ask;\n    }\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/WebsocketFeed.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.coinbase.exchange.security.Signature;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.websocket.ClientEndpoint;\nimport javax.websocket.CloseReason;\nimport javax.websocket.ContainerProvider;\nimport javax.websocket.OnClose;\nimport javax.websocket.OnError;\nimport javax.websocket.OnMessage;\nimport javax.websocket.OnOpen;\nimport javax.websocket.Session;\nimport javax.websocket.WebSocketContainer;\nimport java.net.URI;\nimport java.time.Instant;\nimport java.util.List;\n\n/**\n * Websocketfeed adapted from someone else's code\n * <p>\n * >Jiji Sasidharan\n */\n@ClientEndpoint\npublic class WebsocketFeed {\n\n    private static final Logger log = LoggerFactory.getLogger(WebsocketFeed.class);\n\n    private final String websocketUrl;\n    private final String passphrase;\n    private final String key;\n    private final boolean guiEnabled;\n    private final Signature signature;\n\n    private final ObjectMapper objectMapper;\n    private Session userSession;\n    private MessageHandler messageHandler;\n\n    public WebsocketFeed(final String websocketUrl,\n                         final String key,\n                         final String passphrase,\n                         final boolean guiEnabled,\n                         final Signature signature,\n                         final ObjectMapper objectMapper) {\n        this.key = key;\n        this.passphrase = passphrase;\n        this.websocketUrl = websocketUrl;\n        this.signature = signature;\n        this.guiEnabled = guiEnabled;\n        this.objectMapper = objectMapper.registerModule(new JavaTimeModule());;\n    }\n\n    public void connect() {\n        if (guiEnabled) {\n            try {\n                WebSocketContainer container = ContainerProvider.getWebSocketContainer();\n                container.setDefaultMaxBinaryMessageBufferSize(1024 * 1024);\n                container.setDefaultMaxTextMessageBufferSize(1024 * 1024);\n                container.connectToServer(this, new URI(websocketUrl));\n            } catch (Exception e) {\n                log.error(\"Could not connect to remote server\", e);\n            }\n        }\n    }\n\n    /**\n     * Callback hook for Connection open events.\n     *\n     * @param userSession the userSession which is opened.\n     */\n    @OnOpen\n    public void onOpen(Session userSession) {\n        log.info(\"opened websocket\");\n        this.userSession = userSession;\n    }\n\n    /**\n     * Callback hook for Connection close events.\n     *\n     * @param userSession the userSession which is getting closed.\n     * @param reason      the reason for connection close\n     */\n    @OnClose\n    public void onClose(Session userSession, CloseReason reason) {\n        log.info(\"closing websocket {}\", reason);\n        this.userSession = null;\n    }\n\n    @OnError\n    public void onError(Throwable t) {\n        log.error(\"websocket error\", t);\n    }\n\n\n    /**\n     * Callback hook for OrderBookMessage Events. This method will be invoked when a client send a message.\n     *\n     * @param message The text message\n     */\n    @OnMessage\n    public void onMessage(String message) {\n        if (this.messageHandler != null) {\n            this.messageHandler.handleMessage(message);\n        }\n    }\n\n    /**\n     * register message handler\n     *\n     * @param msgHandler\n     */\n    public void addMessageHandler(MessageHandler msgHandler) {\n        this.messageHandler = msgHandler;\n    }\n\n    /**\n     * Send a message.\n     *\n     * @param message\n     */\n    public void sendMessage(String message) {\n        log.info(\"send: \" + message);\n        this.userSession.getAsyncRemote().sendText(message);\n    }\n\n\n    public void subscribe(Subscribe msg) {\n        String jsonSubscribeMessage = signObject(msg);\n        log.info(jsonSubscribeMessage);\n        // send subscription message to websocket\n        sendMessage(jsonSubscribeMessage);\n    }\n\n    // TODO - get this into postHandle interceptor.\n    public String signObject(Subscribe jsonObj) {\n        String jsonString = toJson(jsonObj);\n\n        String timestamp = Instant.now().getEpochSecond() + \"\";\n        jsonObj.setKey(key);\n        jsonObj.setTimestamp(timestamp);\n        jsonObj.setPassphrase(passphrase);\n        jsonObj.setSignature(signature.generate(\"\", \"GET\", jsonString, timestamp));\n\n        return toJson(jsonObj);\n    }\n\n    private String toJson(Object object) {\n        try {\n            String json = objectMapper.writeValueAsString(object);\n            return json;\n        } catch (JsonProcessingException e) {\n            log.error(\"Unable to serialize\", e);\n            throw new RuntimeException(\"Unable to serialize\");\n        }\n    }\n\n\n    public List<OrderBookMessage> getOrdersAfter(Long sequenceId) {\n        return messageHandler.getQueuedMessages(sequenceId);\n    }\n\n\n    /**\n     * OrderBookMessage handler.\n     *\n     * @author Jiji_Sasidharan\n     */\n    public interface MessageHandler {\n        public void handleMessage(String message);\n\n        List<OrderBookMessage> getQueuedMessages(Long sequenceId);\n    }\n}\n"
  },
  {
    "path": "websocketfeed/src/main/java/com/coinbase/exchange/websocketfeed/WebsocketMessageHandler.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.CompletableFuture;\n\nimport static java.util.stream.Collectors.toList;\n\npublic class WebsocketMessageHandler implements WebsocketFeed.MessageHandler {\n\n    private static final String FILLED = \"filled\";\n    private static final Logger log = LoggerFactory.getLogger(WebsocketMessageHandler.class);\n\n    private final ObjectMapper objectMapper;\n    private final List<OrderBookMessage> orderMessageQueue;\n\n    public WebsocketMessageHandler(final ObjectMapper objectMapper){\n        this.objectMapper = objectMapper;\n        this.orderMessageQueue = new ArrayList<>();\n    }\n\n    @Override\n    public void handleMessage(final String json) {\n        CompletableFuture.runAsync(() -> {\n            FeedMessage message = getObject(json, FeedMessage.class);\n\n            if (message instanceof OrderBookMessage) {\n                orderMessageQueue.add((OrderBookMessage) message);\n            } else {\n                log.warn(\"IGNORED MESSAGE: {}\", message);\n            }\n\n            if (message instanceof HeartBeat) {\n                handleHeartbeat((HeartBeat) message);\n\n            } else if (message instanceof ReceivedOrderBookMessage) {\n                handleReceived(json);\n\n            } else if (message instanceof OpenedOrderBookMessage) {\n                handleOpened((OpenedOrderBookMessage) message, json);\n\n            } else if (message instanceof DoneOrderBookMessage) {\n                handleDone((DoneOrderBookMessage) message, json);\n\n            } else if (message instanceof MatchedOrderBookMessage) {\n                handleMatched(json);\n\n            } else if (message instanceof ChangedOrderBookMessage) {\n                handleChanged(json);\n\n            } else if (message instanceof ErrorOrderBookMessage) {\n                handleError((ErrorOrderBookMessage) message, json);\n\n            } else {\n                log.error(\"Unsupported message type {}\", json);\n            }\n        });\n    }\n\n    @Override\n    public List<OrderBookMessage> getQueuedMessages(Long sequenceId) {\n        return orderMessageQueue.stream().filter(msg -> msg.getSequence().compareTo(sequenceId)<0).collect(toList());\n    }\n\n    private void handleError(ErrorOrderBookMessage message, String json) {\n        // Not sure this is required unless I'm attempting to place orders\n        // ERROR\n        log.warn(\"Error {}\", json);\n        ErrorOrderBookMessage errorMessage = message;\n    }\n\n    private void handleChanged(String json) {\n        // TODO - possibly need to provide implementation for this to work in real time.\n        log.info(\"CHANGED {}\", json);\n    }\n\n    private void handleMatched(String json) {\n        log.info(\"MATCHED: {}\", json);\n        OrderBookMessage matchedOrder = getObject(json, MatchedOrderBookMessage.class);\n    }\n\n    private void handleDone(DoneOrderBookMessage message, String json) {\n        log.info(\"DONE: {}\", json);\n        DoneOrderBookMessage doneOrder = message;\n    }\n\n    private void handleOpened(OpenedOrderBookMessage message, String json) {\n        log.info(\"OPENED: {}\", json);\n        OpenedOrderBookMessage openOrderBookMessage = message;\n    }\n\n    private void handleReceived(String json) {\n        // received orders are not necessarily live orders -\n        // so safe to ignore these msgs as they're unnecessary\n        // https://docs.pro.coinbase.com/#the-full-channel see here for more details\n\n    }\n\n    private void handleHeartbeat(HeartBeat message) {\n        HeartBeat heartBeat = message;\n        log.info(\"HEARTBEAT. Last trade id: {}\", heartBeat.getLast_trade_id());\n    }\n\n\n    public <T> T getObject(String json, Class<T> type) {\n        try {\n            return objectMapper.readValue(json, type);\n        } catch (IOException e) {\n            log.error(\"Parsing {} Failed: {}\", type, e);\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/ActivateOrderBookMessageTest.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport org.junit.jupiter.api.Test;\n\nimport java.math.BigDecimal;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n\nclass ActivateOrderBookMessageTest {\n\n    @Test\n    void shouldDeserialiseActivateMessagesFromJsonSuccessfully() throws JsonProcessingException {\n        ObjectMapper objectMapper = new ObjectMapper();\n        objectMapper.registerModule(new JavaTimeModule());\n        String json = \"{\" +\n                \"           \\\"type\\\": \\\"activate\\\",\" +\n                \"           \\\"product_id\\\": \\\"BTC-GBP\\\",\" +\n                \"           \\\"timestamp\\\": \\\"1483736448.299000\\\",\" +\n                \"           \\\"user_id\\\": \\\"12\\\",\" +\n                \"           \\\"profile_id\\\": \\\"30000727-d308-cf50-7b1c-c06deb1934fc\\\",\" +\n                \"           \\\"order_id\\\": \\\"7b52009b-64fd-0a2a-49e6-d8a939753077\\\",\" +\n                \"           \\\"stop_type\\\": \\\"entry\\\",\" +\n                \"           \\\"side\\\": \\\"buy\\\",\" +\n                \"           \\\"stop_price\\\": \\\"80\\\",\" +\n                \"           \\\"size\\\": \\\"2\\\",\" +\n                \"           \\\"funds\\\": \\\"50\\\",\" +\n                \"           \\\"private\\\": true\" +\n                \"}\";\n\n        ActivateOrderBookMessage result = objectMapper.readValue(json, ActivateOrderBookMessage.class);\n\n        assertEquals(\"activate\", result.getType());\n        assertEquals(\"BTC-GBP\", result.getProduct_id());\n        assertEquals(1483736448, result.getTimestamp().getEpochSecond());\n        assertEquals(299000000, result.getTimestamp().getNano());\n        assertEquals(\"12\", result.getUser_id());\n        assertEquals(\"30000727-d308-cf50-7b1c-c06deb1934fc\", result.getProfile_id());\n        assertEquals(\"7b52009b-64fd-0a2a-49e6-d8a939753077\", result.getOrder_id());\n        assertEquals(\"entry\", result.getStop_type());\n        assertEquals(\"buy\", result.getSide());\n        assertEquals(new BigDecimal(80), result.getStop_price());\n        assertEquals(new BigDecimal(2), result.getSize());\n        assertEquals(new BigDecimal(50), result.getFunds());\n        assertTrue(result.isPrivateFlag());\n    }\n\n}"
  },
  {
    "path": "websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/ChangedOrderBookMessageTest.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport org.junit.jupiter.api.Test;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass ChangedOrderBookMessageTest {\n\n    @Test\n    void shouldDeserialiseChangedMessagesFromJsonSuccessfully() throws JsonProcessingException {\n        ObjectMapper objectMapper = new ObjectMapper();\n        objectMapper.registerModule(new JavaTimeModule());\n        String json = \"{\" +\n                \"    \\\"type\\\": \\\"change\\\",\" +\n                \"    \\\"time\\\": \\\"2014-11-07T08:19:27.028459Z\\\",\" +\n                \"    \\\"sequence\\\": 80,\" +\n                \"    \\\"order_id\\\": \\\"ac928c66-ca53-498f-9c13-a110027a60e8\\\",\" +\n                \"    \\\"product_id\\\": \\\"BTC-USD\\\",\" +\n                \"    \\\"new_funds\\\": \\\"5.23512\\\",\" +\n                \"    \\\"old_funds\\\": \\\"12.234412\\\",\" +\n                \"    \\\"price\\\": \\\"400.23\\\",\" +\n                \"    \\\"side\\\": \\\"sell\\\"\" +\n                \"}\";\n\n        ChangedOrderBookMessage result = objectMapper.readValue(json, ChangedOrderBookMessage.class);\n\n        assertEquals(\"change\", result.getType());\n        assertEquals(\"2014-11-07T08:19:27.028459Z\", result.getTime().toString());\n        assertEquals(80L, result.getSequence());\n        assertEquals(\"ac928c66-ca53-498f-9c13-a110027a60e8\", result.getOrder_id());\n        assertEquals(\"BTC-USD\", result.getProduct_id());\n        assertEquals(new BigDecimal(5.23512).setScale(5, RoundingMode.HALF_UP), result.getNew_funds());\n        assertEquals(new BigDecimal(12.234412).setScale(6, RoundingMode.HALF_UP), result.getOld_funds());\n        assertEquals(new BigDecimal(400.23).setScale(2, RoundingMode.HALF_UP), result.getPrice());\n        assertEquals(\"sell\", result.getSide());\n    }\n}"
  },
  {
    "path": "websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/DoneOrderBookMessageTest.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport org.junit.jupiter.api.Test;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass DoneOrderBookMessageTest {\n\n    @Test\n    void shouldDeserialiseDoneMessagesFromJsonSuccessfully() throws JsonProcessingException {\n        ObjectMapper objectMapper = new ObjectMapper();\n        objectMapper.registerModule(new JavaTimeModule());\n        String json = \" {\" +\n                \"   \\\"type\\\": \\\"done\\\",\" +\n                \"   \\\"time\\\": \\\"2014-11-07T08:19:27.028459Z\\\",\" +\n                \"   \\\"product_id\\\": \\\"BTC-USD\\\",\" +\n                \"   \\\"sequence\\\": 10,\" +\n                \"   \\\"price\\\": \\\"200.2\\\",\" +\n                \"   \\\"order_id\\\": \\\"d50ec984-77a8-460a-b958-66f114b0de9b\\\",\" +\n                \"   \\\"reason\\\": \\\"filled\\\", \" + // canceled\n                \"   \\\"side\\\": \\\"sell\\\",\" +\n                \"   \\\"remaining_size\\\": \\\"0.2\\\"\" +\n                \" }\";\n\n        DoneOrderBookMessage result = objectMapper.readValue(json, DoneOrderBookMessage.class);\n\n        assertEquals(\"done\", result.getType());\n        assertEquals(\"2014-11-07T08:19:27.028459Z\", result.getTime().toString());\n        assertEquals(\"BTC-USD\", result.getProduct_id());\n        assertEquals(10L, result.getSequence());\n        assertEquals(new BigDecimal(200.2).setScale(1, RoundingMode.HALF_UP), result.getPrice());\n        assertEquals(\"d50ec984-77a8-460a-b958-66f114b0de9b\", result.getOrder_id());\n        assertEquals(\"filled\", result.getReason());\n        assertEquals(\"sell\", result.getSide());\n        assertEquals(new BigDecimal(0.2).setScale(1, RoundingMode.HALF_UP), result.getRemaining_size());\n    }\n}"
  },
  {
    "path": "websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/ErrorOrderBookMessageTest.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass ErrorOrderBookMessageTest {\n\n    ObjectMapper objectMapper = new ObjectMapper();\n\n    @Test\n    void shouldDeserialiseAllFieldsForErrorOrderBookMessage() throws JsonProcessingException {\n        String json = \"{ \\\"type\\\": \\\"error\\\", \\\"message\\\": \\\"error message\\\" }\";\n\n        ErrorOrderBookMessage errorMessage = objectMapper.readValue(json, ErrorOrderBookMessage.class);\n\n        assertEquals(\"error\", errorMessage.getType());\n        assertEquals(\"error message\", errorMessage.getMessage());\n\n    }\n\n\n\n}"
  },
  {
    "path": "websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/HeartBeatTest.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass HeartBeatTest {\n\n    @Test\n    void shouldDeserialiseHeartbeatMessageSuccessfully() throws JsonProcessingException {\n        ObjectMapper objectMapper = new ObjectMapper();\n        objectMapper.registerModule(new JavaTimeModule());\n        String json = \"{\" +\n                \"    \\\"type\\\": \\\"heartbeat\\\",                 \" +\n                \"    \\\"sequence\\\": 90,                      \" +\n                \"    \\\"last_trade_id\\\": 20,\" +\n                \"    \\\"product_id\\\": \\\"BTC-USD\\\",             \" +\n                \"    \\\"time\\\": \\\"2014-11-07T08:19:28.464459Z\\\"\" +\n                \"}\";\n\n        HeartBeat result = objectMapper.readValue(json, HeartBeat.class);\n\n        assertEquals(\"heartbeat\", result.getType());\n        assertEquals(90L, result.getSequence());\n        assertEquals(20, result.getLast_trade_id());\n        assertEquals(\"BTC-USD\", result.getProduct_id());\n        assertEquals(\"2014-11-07T08:19:28.464459Z\", result.getTime().toString());\n\n    }\n}"
  },
  {
    "path": "websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/L2UpdateMessageTest.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass L2UpdateMessageTest {\n\n    @Test\n    void shouldDeserialiseL2UpdateMessagesSuccessfully() throws JsonProcessingException {\n        ObjectMapper objectMapper = new ObjectMapper();\n        objectMapper.registerModule(new JavaTimeModule());\n        String json = \"{ \\\"type\\\": \\\"l2update\\\",\" +\n                \"  \\\"product_id\\\": \\\"BTC-GBP\\\",\" +\n                \"  \\\"changes\\\": [\" +\n                \"    [\" +\n                \"      \\\"buy\\\",\" +\n                \"      \\\"5454.12\\\",\" +\n                \"      \\\"0.00000000\\\"\" +\n                \"    ]\" +\n                \"  ],\" +\n                \"  \\\"time\\\": \\\"2020-04-10T15:28:07.393966Z\\\"\" +\n                \"}\";\n\n        L2UpdateMessage result = objectMapper.readValue(json, L2UpdateMessage.class);\n\n        assertEquals(\"l2update\", result.getType());\n        assertEquals(\"BTC-GBP\", result.getProduct_id());\n        assertEquals(\"buy\", result.getChanges()[0][0]);\n        assertEquals(\"5454.12\", result.getChanges()[0][1]);\n        assertEquals(\"0.00000000\", result.getChanges()[0][2]);\n        assertEquals(\"2020-04-10T15:28:07.393966Z\", result.getTime().toString());\n    }\n}"
  },
  {
    "path": "websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/MatchedOrderBookMessageTest.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport org.junit.jupiter.api.Test;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass MatchedOrderBookMessageTest {\n\n    @Test\n    void shouldDeserialiseMatchedMessagesFromJsonSuccessfully() throws JsonProcessingException {\n        ObjectMapper objectMapper = new ObjectMapper();\n        objectMapper.registerModule(new JavaTimeModule());\n        String json = \" {\" +\n                \"   \\\"type\\\": \\\"match\\\",\" +\n                \"   \\\"trade_id\\\": 10,\" +\n                \"   \\\"sequence\\\": 50,\" +\n                \"   \\\"maker_order_id\\\": \\\"ac928c66-ca53-498f-9c13-a110027a60e8\\\",\" +\n                \"   \\\"taker_order_id\\\": \\\"132fb6ae-456b-4654-b4e0-d681ac05cea1\\\",\" +\n                \"   \\\"time\\\": \\\"2014-11-07T08:19:27.028459Z\\\",\" +\n                \"   \\\"product_id\\\": \\\"BTC-USD\\\",\" +\n                \"   \\\"size\\\": \\\"5.23512\\\",\" +\n                \"   \\\"price\\\": \\\"400.23\\\",\" +\n                \"   \\\"side\\\": \\\"sell\\\"\" +\n                \" }\";\n\n        MatchedOrderBookMessage result = objectMapper.readValue(json, MatchedOrderBookMessage.class);\n\n        assertEquals(\"match\", result.getType());\n        assertEquals(\"10\", result.getTrade_id());\n        assertEquals(50L, result.getSequence());\n        assertEquals(\"ac928c66-ca53-498f-9c13-a110027a60e8\", result.getMaker_order_id());\n        assertEquals(\"132fb6ae-456b-4654-b4e0-d681ac05cea1\", result.getTaker_order_id());\n        assertEquals(\"2014-11-07T08:19:27.028459Z\", result.getTime().toString());\n        assertEquals(\"BTC-USD\", result.getProduct_id());\n        assertEquals(new BigDecimal(5.23512).setScale(5, RoundingMode.HALF_UP), result.getSize());\n        assertEquals(new BigDecimal(400.23).setScale(2, RoundingMode.HALF_UP), result.getPrice());\n        assertEquals(\"sell\", result.getSide());\n    }\n\n}"
  },
  {
    "path": "websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/OpenedOrderBookMessageTest.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport org.junit.jupiter.api.Test;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass OpenedOrderBookMessageTest {\n\n    @Test\n    void shouldDeserialiseOpenedMessagesFromJsonSuccessfully() throws JsonProcessingException {\n        ObjectMapper objectMapper = new ObjectMapper();\n        objectMapper.registerModule(new JavaTimeModule());\n        String json = \"{\" +\n                \"  \\\"type\\\": \\\"open\\\",\" +\n                \"  \\\"time\\\": \\\"2014-11-07T08:19:27.028459Z\\\",\" +\n                \"  \\\"product_id\\\": \\\"BTC-USD\\\",\" +\n                \"  \\\"sequence\\\": 10,\" +\n                \"  \\\"order_id\\\": \\\"d50ec984-77a8-460a-b958-66f114b0de9b\\\",\" +\n                \"  \\\"price\\\": \\\"200.2\\\",\" +\n                \"  \\\"remaining_size\\\": \\\"1.00\\\",\" +\n                \"  \\\"side\\\": \\\"sell\\\"\" +\n                \"}\";\n\n        OpenedOrderBookMessage result = objectMapper.readValue(json, OpenedOrderBookMessage.class);\n\n        assertEquals(\"open\", result.getType());\n        assertEquals(\"2014-11-07T08:19:27.028459Z\", result.getTime().toString());\n        assertEquals(\"BTC-USD\", result.getProduct_id());\n        assertEquals(10L, result.getSequence());\n        assertEquals(\"d50ec984-77a8-460a-b958-66f114b0de9b\", result.getOrder_id());\n        assertEquals(new BigDecimal(1.00).setScale(2, RoundingMode.HALF_UP), result.getRemaining_size());\n        assertEquals(new BigDecimal(200.2).setScale(1, RoundingMode.HALF_UP), result.getPrice());\n        assertEquals(\"sell\", result.getSide());\n    }\n\n}"
  },
  {
    "path": "websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/ReceivedOrderBookMessageTest.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport org.junit.jupiter.api.Test;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass ReceivedOrderBookMessageTest {\n\n    @Test\n    void shouldDeserialiseReceivedMessagesFromJsonSuccessfully() throws JsonProcessingException {\n        ObjectMapper objectMapper = new ObjectMapper();\n        objectMapper.registerModule(new JavaTimeModule());\n        String json = \"{\" +\n                \"    \\\"type\\\": \\\"received\\\",\" +\n                \"    \\\"time\\\": \\\"2014-11-07T08:19:27.028459Z\\\",\" +\n                \"    \\\"product_id\\\": \\\"BTC-USD\\\",\" +\n                \"    \\\"sequence\\\": 10,\" +\n                \"    \\\"order_id\\\": \\\"d50ec984-77a8-460a-b958-66f114b0de9b\\\",\" +\n                \"    \\\"size\\\": \\\"1.34\\\",\" +\n                \"    \\\"price\\\": \\\"502.1\\\",\" +\n                \"    \\\"side\\\": \\\"buy\\\",\" +\n                \"    \\\"order_type\\\": \\\"limit\\\"\" +\n                \"  }\";\n\n        ReceivedOrderBookMessage result = objectMapper.readValue(json, ReceivedOrderBookMessage.class);\n\n        assertEquals(\"received\", result.getType());\n        assertEquals(\"2014-11-07T08:19:27.028459Z\", result.getTime().toString());\n        assertEquals(\"BTC-USD\", result.getProduct_id());\n        assertEquals(10L, result.getSequence());\n        assertEquals(\"d50ec984-77a8-460a-b958-66f114b0de9b\", result.getOrder_id());\n        assertEquals(new BigDecimal(1.34).setScale(2, RoundingMode.HALF_UP), result.getSize());\n        assertEquals(new BigDecimal(502.1).setScale(1, RoundingMode.HALF_UP), result.getPrice());\n        assertEquals(\"buy\", result.getSide());\n        assertEquals(\"limit\", result.getOrder_type());\n    }\n\n}"
  },
  {
    "path": "websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/SnapshotMessageTest.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass SnapshotMessageTest {\n\n    @Test\n    void shouldDeserialiseJsonSuccessfullyForValidJson() throws JsonProcessingException {\n        ObjectMapper objectMapper = new ObjectMapper();\n        String json = \"{\" +\n                \"    \\\"type\\\": \\\"snapshot\\\",\" +\n                \"    \\\"product_id\\\": \\\"BTC-USD\\\",\" +\n                \"    \\\"bids\\\": [[\\\"10101.10\\\", \\\"0.45054140\\\"]],\" +\n                \"    \\\"asks\\\": [[\\\"10102.55\\\", \\\"0.57753524\\\"]]\" +\n                \"}\";\n\n        SnapshotMessage result = objectMapper.readValue(json, SnapshotMessage.class);\n\n        assertEquals(\"snapshot\", result.getType());\n        assertEquals(\"BTC-USD\", result.getProduct_id());\n        assertArrayEquals(new String[][] { new String[]{\"10101.10\", \"0.45054140\"}}, result.getBids());\n        assertArrayEquals(new String[][] { new String[]{\"10102.55\", \"0.57753524\"}}, result.getAsks());\n    }\n}"
  },
  {
    "path": "websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/StatusMessageTest.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport org.junit.jupiter.api.Test;\n\nimport java.math.BigDecimal;\nimport java.util.Collections;\n\nimport static java.math.RoundingMode.HALF_UP;\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass StatusMessageTest {\n\n    @Test\n    void shouldDeserialiseSuccessfullyFromValidJson() throws JsonProcessingException {\n        ObjectMapper objectMapper = new ObjectMapper();\n        objectMapper.registerModule(new JavaTimeModule());\n        String json = \"{\" +\n                \"    \\\"type\\\": \\\"status\\\",\" +\n                \"    \\\"products\\\": [\" +\n                \"        {\" +\n                \"            \\\"id\\\": \\\"BTC-USD\\\",\" +\n                \"            \\\"base_currency\\\": \\\"BTC\\\",\" +\n                \"            \\\"quote_currency\\\": \\\"USD\\\",\" +\n                \"            \\\"base_min_size\\\": \\\"0.001\\\",\" +\n                \"            \\\"base_max_size\\\": \\\"70\\\",\" +\n                \"            \\\"base_increment\\\": \\\"0.00000001\\\",\" +\n                \"            \\\"quote_increment\\\": \\\"0.01\\\",\" +\n                \"            \\\"display_name\\\": \\\"BTC/USD\\\",\" +\n                \"            \\\"status\\\": \\\"online\\\",\" +\n                \"            \\\"status_message\\\": \\\"some status message1\\\",\" +\n                \"            \\\"min_market_funds\\\": \\\"10\\\",\" +\n                \"            \\\"max_market_funds\\\": \\\"1000000\\\",\" +\n                \"            \\\"post_only\\\": false,\" +\n                \"            \\\"limit_only\\\": false,\" +\n                \"            \\\"cancel_only\\\": false\" +\n                \"        }\" +\n                \"    ],\" +\n                \"    \\\"currencies\\\": [\" +\n                \"        {\" +\n                \"            \\\"id\\\": \\\"USD\\\",\" +\n                \"            \\\"name\\\": \\\"United States Dollar\\\",\" +\n                \"            \\\"min_size\\\": \\\"0.01000000\\\",\" +\n                \"            \\\"status\\\": \\\"online\\\",\" +\n                \"            \\\"status_message\\\": \\\"some status message2\\\",\" +\n                \"            \\\"max_precision\\\": \\\"0.01\\\",\" +\n                \"            \\\"convertible_to\\\": [\\\"USDC\\\"],\" +\n                \"            \\\"details\\\": {}\" +\n                \"        },\" +\n                \"        {\" +\n                \"            \\\"id\\\": \\\"USDC\\\",\" +\n                \"            \\\"name\\\": \\\"USD Coin\\\",\" +\n                \"            \\\"min_size\\\": \\\"0.00000100\\\",\" +\n                \"            \\\"status\\\": \\\"online\\\",\" +\n                \"            \\\"status_message\\\": null,\" +\n                \"            \\\"max_precision\\\": \\\"0.000001\\\",\" +\n                \"            \\\"convertible_to\\\": [\\\"USD\\\"], \" +\n                \"            \\\"details\\\": {}\" +\n                \"        },\" +\n                \"        {\" +\n                \"            \\\"id\\\": \\\"BTC\\\",\" +\n                \"            \\\"name\\\": \\\"Bitcoin\\\",\" +\n                \"            \\\"min_size\\\":\\\" 0.00000001\\\",\" +\n                \"            \\\"status\\\": \\\"online\\\",\" +\n                \"            \\\"status_message\\\": null,\" +\n                \"            \\\"max_precision\\\": \\\"0.00000001\\\",\" +\n                \"            \\\"convertible_to\\\": []\" +\n                \"        }\" +\n                \"    ]\" +\n                \"}\";\n\n        StatusMessage result = objectMapper.readValue(json, StatusMessage.class);\n\n        assertEquals(\"status\", result.getType());\n\n        assertEquals(\"BTC-USD\", result.getProducts()[0].getId());\n        assertEquals(\"BTC\", result.getProducts()[0].getBase_currency());\n        assertEquals(\"USD\", result.getProducts()[0].getQuote_currency());\n        assertEquals(0.001d, result.getProducts()[0].getBase_min_size());\n        assertEquals(70d, result.getProducts()[0].getBase_max_size());\n        assertEquals(0.00000001d, result.getProducts()[0].getBase_increment());\n        assertEquals(0.01d, result.getProducts()[0].getQuote_increment());\n        assertEquals(\"BTC/USD\", result.getProducts()[0].getDisplay_name());\n        assertEquals(\"online\", result.getProducts()[0].getStatus());\n        assertEquals(\"some status message1\", result.getProducts()[0].getStatus_message());\n        assertEquals(10, result.getProducts()[0].getMin_market_funds());\n        assertEquals(1000000, result.getProducts()[0].getMax_market_funds());\n        assertEquals(false, result.getProducts()[0].getPost_only());\n        assertEquals(false, result.getProducts()[0].getLimit_only());\n        assertEquals(false, result.getProducts()[0].getCancel_only());\n\n        assertEquals(\"USD\", result.getCurrencies()[0].getId());\n        assertEquals(\"United States Dollar\", result.getCurrencies()[0].getName());\n        assertEquals(new BigDecimal(0.01000000).setScale(8, HALF_UP), result.getCurrencies()[0].getMin_size());\n        assertEquals(\"online\", result.getCurrencies()[0].getStatus());\n        assertEquals(\"some status message2\", result.getCurrencies()[0].getStatus_message());\n        assertEquals(new BigDecimal(0.01).setScale(2, HALF_UP), result.getCurrencies()[0].getMax_precision());\n        assertArrayEquals(new String[]{\"USDC\"}, result.getCurrencies()[0].getConvertible_to());\n        assertEquals(Collections.EMPTY_MAP, result.getCurrencies()[0].getDetails());\n\n\n        assertEquals(\"USDC\", result.getCurrencies()[1].getId());\n        assertEquals(\"USD Coin\", result.getCurrencies()[1].getName());\n        assertEquals(new BigDecimal(0.00000100).setScale(8, HALF_UP), result.getCurrencies()[1].getMin_size());\n        assertEquals(\"online\", result.getCurrencies()[1].getStatus());\n        assertEquals(null, result.getCurrencies()[1].getStatus_message());\n        assertEquals(new BigDecimal(0.000001).setScale(6, HALF_UP), result.getCurrencies()[1].getMax_precision());\n        assertArrayEquals(new String[]{\"USD\"}, result.getCurrencies()[1].getConvertible_to());\n        assertEquals(Collections.EMPTY_MAP, result.getCurrencies()[1].getDetails());\n\n        assertEquals(\"BTC\", result.getCurrencies()[2].getId());\n        assertEquals(\"Bitcoin\", result.getCurrencies()[2].getName());\n        assertEquals( new BigDecimal(0.00000001).setScale(8, HALF_UP), result.getCurrencies()[2].getMin_size());\n        assertEquals(\"online\", result.getCurrencies()[2].getStatus());\n        assertEquals(null, result.getCurrencies()[2].getStatus_message());\n        assertEquals(new BigDecimal(0.00000001).setScale(8, HALF_UP), result.getCurrencies()[2].getMax_precision());\n        assertArrayEquals(new String[]{}, result.getCurrencies()[2].getConvertible_to());\n        assertEquals(null, result.getCurrencies()[2].getDetails());\n    }\n}"
  },
  {
    "path": "websocketfeed/src/test/java/com/coinbase/exchange/websocketfeed/TickerMessageTest.java",
    "content": "package com.coinbase.exchange.websocketfeed;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport org.junit.jupiter.api.Test;\n\nimport java.math.BigDecimal;\n\nimport static java.math.RoundingMode.HALF_UP;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass TickerMessageTest {\n\n    @Test\n    void shouldDeserialiseSuccessfullyFromValidJson() throws JsonProcessingException {\n        ObjectMapper objectMapper = new ObjectMapper();\n        objectMapper.registerModule(new JavaTimeModule());\n        String json = \"{\" +\n                \"    \\\"type\\\": \\\"ticker\\\",\" +\n                \"    \\\"trade_id\\\": 20153558,\" +\n                \"    \\\"sequence\\\": 3262786978,\" +\n                \"    \\\"time\\\": \\\"2017-09-02T17:05:49.250000Z\\\",\" +\n                \"    \\\"product_id\\\": \\\"BTC-USD\\\",\" +\n                \"    \\\"price\\\": \\\"4388.01000000\\\",\" +\n                \"    \\\"side\\\": \\\"buy\\\",\" +\n                \"    \\\"last_size\\\": \\\"0.03000000\\\",\" +\n                \"    \\\"best_bid\\\": \\\"4388\\\",\" +\n                \"    \\\"best_ask\\\": \\\"4388.01\\\"\" +\n                \"}\";\n\n        TickerMessage result = objectMapper.readValue(json, TickerMessage.class);\n\n        assertEquals(\"ticker\", result.getType());\n        assertEquals(20153558L, result.getTrade_id());\n        assertEquals(3262786978L, result.getSequence());\n        assertEquals(\"2017-09-02T17:05:49.250Z\", result.getTime().toString());\n        assertEquals(\"BTC-USD\", result.getProduct_id());\n        assertEquals(new BigDecimal(4388.01000000).setScale(8, HALF_UP), result.getPrice());\n        assertEquals(\"buy\", result.getSide());\n        assertEquals(new BigDecimal(4388).setScale(0, HALF_UP), result.getBest_bid());\n        assertEquals(new BigDecimal(4388.01).setScale(2, HALF_UP), result.getBest_ask());\n\n\n    }\n}"
  }
]