From b843cbb62e061d163c9b6b2f64b36b5468cd3c0a Mon Sep 17 00:00:00 2001 From: gazbert Date: Sat, 6 Feb 2021 13:12:16 +0000 Subject: [PATCH] Merge Coinbase Pro exchange adapter to master (#134) * Merge David Huertas Coinbase Pro exchange adapter work into new coinbase-pro feature branch (#133) * Created Coinbase Pro Exchange Adapter (#1) * Added PowerMock tests to Coinbase Pro ExchangeAdapter, version working, closes #1 Co-authored-by: David Huertas * #132 : Updated Travis badge * #132 : Exclude SpotBugs false positives + checkstyle fixes * #132 : Removed deprecated OKCoin exchange adapter * #132 : Added Coinbase Pro adapter to Gradle coverage exclusions * #132 : Add author to MIT * #132 : Added IT test * #132 : README update * #132 : README tweak * #132 : Removed GDAX code + config; added CoinbasePro sample config * #132 : Corrected location of time-server-bias config Co-authored-by: David Huertas --- README.md | 5 +- bxbot-exchanges/build.gradle | 3 +- .../{GdaxIT.java => CoinbaseProIT.java} | 17 +- .../com/gazbert/bxbot/exchanges/OkCoinIT.java | 150 -- ...r.java => CoinbaseProExchangeAdapter.java} | 266 +-- .../exchanges/OkCoinExchangeAdapter.java | 899 --------- .../{gdax => coinbasepro}/accounts.json | 0 .../{gdax => coinbasepro}/book.json | 0 .../{gdax => coinbasepro}/cancel.json | 0 .../{gdax => coinbasepro}/new_buy_order.json | 0 .../{gdax => coinbasepro}/new_sell_order.json | 0 .../{gdax => coinbasepro}/orders.json | 0 .../{gdax => coinbasepro}/stats.json | 0 .../{gdax => coinbasepro}/ticker.json | 0 .../okcoin/cancel_order-error.json | 4 - .../exchange-data/okcoin/cancel_order.json | 4 - .../src/test/exchange-data/okcoin/depth.json | 1606 ----------------- .../okcoin/order_info-error.json | 4 - .../test/exchange-data/okcoin/order_info.json | 29 - .../src/test/exchange-data/okcoin/ticker.json | 11 - .../exchange-data/okcoin/trade-error.json | 4 - .../test/exchange-data/okcoin/trade_buy.json | 4 - .../test/exchange-data/okcoin/trade_sell.json | 4 - .../exchange-data/okcoin/userinfo-error.json | 4 - .../test/exchange-data/okcoin/userinfo.json | 21 - ...va => TestCoinbaseProExchangeAdapter.java} | 175 +- .../exchanges/TestOkcoinExchangeAdapter.java | 1397 -------------- .../yaml/TestMarketConfigYamlRepository.java | 2 +- .../TestMarketConfigurationManagement.java | 2 +- .../{gdax => coinbase-pro}/email-alerts.yaml | 2 - .../{gdax => coinbase-pro}/engine.yaml | 12 +- .../{gdax => coinbase-pro}/exchange.yaml | 29 +- .../{gdax => coinbase-pro}/markets.yaml | 2 - .../{gdax => coinbase-pro}/strategies.yaml | 2 - config/samples/okcoin/email-alerts.yaml | 27 - config/samples/okcoin/engine.yaml | 36 - config/samples/okcoin/exchange.yaml | 60 - config/samples/okcoin/markets.yaml | 35 - config/samples/okcoin/strategies.yaml | 47 - etc/spotbugs-exclude-filter.xml | 28 +- 40 files changed, 267 insertions(+), 4624 deletions(-) rename bxbot-exchanges/src/integration-test/java/com/gazbert/bxbot/exchanges/{GdaxIT.java => CoinbaseProIT.java} (92%) delete mode 100644 bxbot-exchanges/src/integration-test/java/com/gazbert/bxbot/exchanges/OkCoinIT.java rename bxbot-exchanges/src/main/java/com/gazbert/bxbot/exchanges/{GdaxExchangeAdapter.java => CoinbaseProExchangeAdapter.java} (77%) delete mode 100644 bxbot-exchanges/src/main/java/com/gazbert/bxbot/exchanges/OkCoinExchangeAdapter.java rename bxbot-exchanges/src/test/exchange-data/{gdax => coinbasepro}/accounts.json (100%) rename bxbot-exchanges/src/test/exchange-data/{gdax => coinbasepro}/book.json (100%) rename bxbot-exchanges/src/test/exchange-data/{gdax => coinbasepro}/cancel.json (100%) rename bxbot-exchanges/src/test/exchange-data/{gdax => coinbasepro}/new_buy_order.json (100%) rename bxbot-exchanges/src/test/exchange-data/{gdax => coinbasepro}/new_sell_order.json (100%) rename bxbot-exchanges/src/test/exchange-data/{gdax => coinbasepro}/orders.json (100%) rename bxbot-exchanges/src/test/exchange-data/{gdax => coinbasepro}/stats.json (100%) rename bxbot-exchanges/src/test/exchange-data/{gdax => coinbasepro}/ticker.json (100%) delete mode 100644 bxbot-exchanges/src/test/exchange-data/okcoin/cancel_order-error.json delete mode 100644 bxbot-exchanges/src/test/exchange-data/okcoin/cancel_order.json delete mode 100644 bxbot-exchanges/src/test/exchange-data/okcoin/depth.json delete mode 100644 bxbot-exchanges/src/test/exchange-data/okcoin/order_info-error.json delete mode 100644 bxbot-exchanges/src/test/exchange-data/okcoin/order_info.json delete mode 100644 bxbot-exchanges/src/test/exchange-data/okcoin/ticker.json delete mode 100644 bxbot-exchanges/src/test/exchange-data/okcoin/trade-error.json delete mode 100644 bxbot-exchanges/src/test/exchange-data/okcoin/trade_buy.json delete mode 100644 bxbot-exchanges/src/test/exchange-data/okcoin/trade_sell.json delete mode 100644 bxbot-exchanges/src/test/exchange-data/okcoin/userinfo-error.json delete mode 100644 bxbot-exchanges/src/test/exchange-data/okcoin/userinfo.json rename bxbot-exchanges/src/test/java/com/gazbert/bxbot/exchanges/{TestGdaxExchangeAdapter.java => TestCoinbaseProExchangeAdapter.java} (89%) delete mode 100644 bxbot-exchanges/src/test/java/com/gazbert/bxbot/exchanges/TestOkcoinExchangeAdapter.java rename config/samples/{gdax => coinbase-pro}/email-alerts.yaml (93%) rename config/samples/{gdax => coinbase-pro}/engine.yaml (88%) rename config/samples/{gdax => coinbase-pro}/exchange.yaml (70%) rename config/samples/{gdax => coinbase-pro}/markets.yaml (96%) rename config/samples/{gdax => coinbase-pro}/strategies.yaml (97%) delete mode 100644 config/samples/okcoin/email-alerts.yaml delete mode 100644 config/samples/okcoin/engine.yaml delete mode 100644 config/samples/okcoin/exchange.yaml delete mode 100644 config/samples/okcoin/markets.yaml delete mode 100644 config/samples/okcoin/strategies.yaml diff --git a/README.md b/README.md index 69c2608f4..d4be60b44 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # BX-bot -[![Build Status](https://travis-ci.com/gazbert/bxbot.svg?branch=master)](https://travis-ci.com/gazbert/bxbot) +[![Build Status](https://travis-ci.com/gazbert/bxbot.svg?branch=coinbase-pro)](https://travis-ci.com/gazbert/bxbot) [![Sonarcloud Status](https://sonarcloud.io/api/project_badges/measure?project=gazbert_bxbot&metric=alert_status)](https://sonarcloud.io/dashboard?id=gazbert_bxbot) [![Join the chat at https://gitter.im/BX-bot/Lobby](https://badges.gitter.im/BX-bot/Lobby.svg)](https://gitter.im/BX-bot/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) @@ -18,7 +18,8 @@ except for the trading strategies - you'll need to write those yourself! A simpl Trading API - take a look [here](https://github.com/ta4j/ta4j) for more ideas. Exchange Adapters for using [Bitstamp](https://www.bitstamp.net), [Bitfinex](https://www.bitfinex.com), -[itBit](https://www.itbit.com/), [Kraken](https://www.kraken.com), and [Gemini](https://gemini.com/) are included. +[itBit](https://www.itbit.com/), [Kraken](https://www.kraken.com), [Gemini](https://gemini.com/), +and [Coinbase Pro](https://pro.coinbase.com/) are included. Feel free to improve these or contribute new adapters to the project; that would be [shiny!](https://en.wikipedia.org/wiki/Firefly_(TV_series)) diff --git a/bxbot-exchanges/build.gradle b/bxbot-exchanges/build.gradle index ba82dff7f..b97fe9d4c 100644 --- a/bxbot-exchanges/build.gradle +++ b/bxbot-exchanges/build.gradle @@ -53,13 +53,12 @@ jacocoTestCoverageVerification { excludes = [ 'com.gazbert.bxbot.exchanges.BitfinexExchangeAdapter*', 'com.gazbert.bxbot.exchanges.BitstampExchangeAdapter*', - 'com.gazbert.bxbot.exchanges.GdaxExchangeAdapter*', 'com.gazbert.bxbot.exchanges.GeminiExchangeAdapter*', 'com.gazbert.bxbot.exchanges.ItBitExchangeAdapter*', 'com.gazbert.bxbot.exchanges.KrakenExchangeAdapter*', - 'com.gazbert.bxbot.exchanges.OkCoinExchangeAdapter*', 'com.gazbert.bxbot.exchanges.TestExchangeAdapter*', 'com.gazbert.bxbot.exchanges.AbstractExchangeAdapter*', + 'com.gazbert.bxbot.exchanges.CoinbaseProExchangeAdapter*', ] limit { counter = 'LINE' diff --git a/bxbot-exchanges/src/integration-test/java/com/gazbert/bxbot/exchanges/GdaxIT.java b/bxbot-exchanges/src/integration-test/java/com/gazbert/bxbot/exchanges/CoinbaseProIT.java similarity index 92% rename from bxbot-exchanges/src/integration-test/java/com/gazbert/bxbot/exchanges/GdaxIT.java rename to bxbot-exchanges/src/integration-test/java/com/gazbert/bxbot/exchanges/CoinbaseProIT.java index 4c7f5e820..deca79c71 100644 --- a/bxbot-exchanges/src/integration-test/java/com/gazbert/bxbot/exchanges/GdaxIT.java +++ b/bxbot-exchanges/src/integration-test/java/com/gazbert/bxbot/exchanges/CoinbaseProIT.java @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016 Gareth Jon Lynch + * Copyright (c) 2021 Gareth Jon Lynch * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -47,15 +47,11 @@ import org.junit.Test; /** - * Basic integration testing with GDAX exchange. - * - *

DO NOT USE: See https://github.com/gazbert/bxbot/pull/120 + * Basic integration testing with Coinbase Pro exchange. * * @author gazbert - * @deprecated #120 : GDAX exchange has been superseded by Coinbase Pro: https://pro.coinbase.com/ */ -@Deprecated(forRemoval = true) -public class GdaxIT { +public class CoinbaseProIT { private static final String MARKET_ID = "BTC-GBP"; private static final BigDecimal BUY_ORDER_PRICE = new BigDecimal("450.176"); @@ -92,6 +88,7 @@ public void setupForEachTest() { otherConfig = createMock(OtherConfig.class); expect(otherConfig.getItem("buy-fee")).andReturn("0.25"); expect(otherConfig.getItem("sell-fee")).andReturn("0.25"); + expect(otherConfig.getItem("time-server-bias")).andReturn("1"); exchangeConfig = createMock(ExchangeConfig.class); expect(exchangeConfig.getAuthenticationConfig()).andReturn(authenticationConfig); @@ -103,7 +100,7 @@ public void setupForEachTest() { public void testPublicApiCalls() throws Exception { replay(authenticationConfig, networkConfig, otherConfig, exchangeConfig); - final ExchangeAdapter exchangeAdapter = new GdaxExchangeAdapter(); + final ExchangeAdapter exchangeAdapter = new CoinbaseProExchangeAdapter(); exchangeAdapter.init(exchangeConfig); assertNotNull(exchangeAdapter.getLatestMarketPrice(MARKET_ID)); @@ -120,7 +117,7 @@ public void testPublicApiCalls() throws Exception { assertNotNull(ticker.getLow()); assertNotNull(ticker.getOpen()); assertNotNull(ticker.getVolume()); - assertNull(ticker.getVwap()); // not provided by GDAX + assertNull(ticker.getVwap()); // not provided by Coinbase Pro assertNotNull(ticker.getTimestamp()); verify(authenticationConfig, networkConfig, otherConfig, exchangeConfig); @@ -134,7 +131,7 @@ public void testPublicApiCalls() throws Exception { public void testAuthenticatedApiCalls() throws Exception { replay(authenticationConfig, networkConfig, otherConfig, exchangeConfig); - final ExchangeAdapter exchangeAdapter = new GdaxExchangeAdapter(); + final ExchangeAdapter exchangeAdapter = new CoinbaseProExchangeAdapter(); exchangeAdapter.init(exchangeConfig); final BalanceInfo balanceInfo = exchangeAdapter.getBalanceInfo(); diff --git a/bxbot-exchanges/src/integration-test/java/com/gazbert/bxbot/exchanges/OkCoinIT.java b/bxbot-exchanges/src/integration-test/java/com/gazbert/bxbot/exchanges/OkCoinIT.java deleted file mode 100644 index b515fc808..000000000 --- a/bxbot-exchanges/src/integration-test/java/com/gazbert/bxbot/exchanges/OkCoinIT.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2016 Gareth Jon Lynch - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.gazbert.bxbot.exchanges; - -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import com.gazbert.bxbot.exchange.api.AuthenticationConfig; -import com.gazbert.bxbot.exchange.api.ExchangeAdapter; -import com.gazbert.bxbot.exchange.api.ExchangeConfig; -import com.gazbert.bxbot.exchange.api.NetworkConfig; -import com.gazbert.bxbot.exchange.api.OtherConfig; -import com.gazbert.bxbot.trading.api.BalanceInfo; -import com.gazbert.bxbot.trading.api.MarketOrderBook; -import com.gazbert.bxbot.trading.api.Ticker; -import java.math.BigDecimal; -import java.util.Arrays; -import java.util.List; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -/** - * Basic integration testing with OKCoin exchange. - * - *

DO NOT USE: See https://github.com/gazbert/bxbot/issues/122 - * - * @author gazbert - */ -@Deprecated(forRemoval = true) -@Ignore("gazbert 26/03/2020 - v1 API is now deprecated and disabled. Adapter needs updating!") -public class OkCoinIT { - - private static final String MARKET_ID = "btc_usd"; - private static final BigDecimal SELL_ORDER_PRICE = new BigDecimal("10000.176"); - private static final BigDecimal SELL_ORDER_QUANTITY = new BigDecimal("0.001"); - - private static final String KEY = "key123"; - private static final String SECRET = "notGonnaTellYa"; - private static final List nonFatalNetworkErrorCodes = Arrays.asList(502, 503, 504); - private static final List nonFatalNetworkErrorMessages = - Arrays.asList( - "Connection refused", - "Connection reset", - "Remote host closed connection during handshake"); - - private ExchangeConfig exchangeConfig; - private AuthenticationConfig authenticationConfig; - private NetworkConfig networkConfig; - private OtherConfig otherConfig; - - /** Create some exchange config - the TradingEngine would normally do this. */ - @Before - public void setupForEachTest() { - authenticationConfig = createMock(AuthenticationConfig.class); - expect(authenticationConfig.getItem("key")).andReturn(KEY); - expect(authenticationConfig.getItem("secret")).andReturn(SECRET); - - networkConfig = createMock(NetworkConfig.class); - expect(networkConfig.getConnectionTimeout()).andReturn(30); - expect(networkConfig.getNonFatalErrorCodes()).andReturn(nonFatalNetworkErrorCodes); - expect(networkConfig.getNonFatalErrorMessages()).andReturn(nonFatalNetworkErrorMessages); - - otherConfig = createMock(OtherConfig.class); - expect(otherConfig.getItem("buy-fee")).andReturn("0.25"); - expect(otherConfig.getItem("sell-fee")).andReturn("0.25"); - - exchangeConfig = createMock(ExchangeConfig.class); - expect(exchangeConfig.getAuthenticationConfig()).andReturn(authenticationConfig); - expect(exchangeConfig.getNetworkConfig()).andReturn(networkConfig); - expect(exchangeConfig.getOtherConfig()).andReturn(otherConfig); - } - - @Test - public void testPublicApiCalls() throws Exception { - replay(authenticationConfig, networkConfig, otherConfig, exchangeConfig); - - final ExchangeAdapter exchangeAdapter = new OkCoinExchangeAdapter(); - exchangeAdapter.init(exchangeConfig); - - assertNotNull(exchangeAdapter.getLatestMarketPrice(MARKET_ID)); - - final MarketOrderBook orderBook = exchangeAdapter.getMarketOrders(MARKET_ID); - assertFalse(orderBook.getBuyOrders().isEmpty()); - assertFalse(orderBook.getSellOrders().isEmpty()); - - final Ticker ticker = exchangeAdapter.getTicker(MARKET_ID); - assertNotNull(ticker.getLast()); - assertNotNull(ticker.getAsk()); - assertNotNull(ticker.getBid()); - assertNotNull(ticker.getHigh()); - assertNotNull(ticker.getLow()); - assertNull(ticker.getOpen()); // open not supplied by OKCoin - assertNotNull(ticker.getVolume()); - assertNull(ticker.getVwap()); // vwap not supplied by OKCoin - assertNotNull(ticker.getTimestamp()); - - verify(authenticationConfig, networkConfig, otherConfig, exchangeConfig); - } - - /* - * You'll need to change the KEY, SECRET, constants to real-world values. - */ - @Ignore("Disabled. Integration testing authenticated API calls requires your secret credentials!") - @Test - public void testAuthenticatedApiCalls() throws Exception { - replay(authenticationConfig, networkConfig, otherConfig, exchangeConfig); - - final ExchangeAdapter exchangeAdapter = new OkCoinExchangeAdapter(); - exchangeAdapter.init(exchangeConfig); - - final BalanceInfo balanceInfo = exchangeAdapter.getBalanceInfo(); - assertNotNull(balanceInfo.getBalancesAvailable().get("BTC")); - - // Careful here: make sure the SELL_ORDER_PRICE is sensible! - // final String orderId = exchangeAdapter.createOrder(MARKET_ID, OrderType.SELL, - // SELL_ORDER_QUANTITY, SELL_ORDER_PRICE); - // final List openOrders = exchangeAdapter.getYourOpenOrders(MARKET_ID); - // assertTrue(openOrders.stream().anyMatch(o -> o.getId().equals(orderId))); - // assertTrue(exchangeAdapter.cancelOrder(orderId, MARKET_ID)); - - verify(authenticationConfig, networkConfig, otherConfig, exchangeConfig); - } -} diff --git a/bxbot-exchanges/src/main/java/com/gazbert/bxbot/exchanges/GdaxExchangeAdapter.java b/bxbot-exchanges/src/main/java/com/gazbert/bxbot/exchanges/CoinbaseProExchangeAdapter.java similarity index 77% rename from bxbot-exchanges/src/main/java/com/gazbert/bxbot/exchanges/GdaxExchangeAdapter.java rename to bxbot-exchanges/src/main/java/com/gazbert/bxbot/exchanges/CoinbaseProExchangeAdapter.java index a6554ed2c..643c787be 100644 --- a/bxbot-exchanges/src/main/java/com/gazbert/bxbot/exchanges/GdaxExchangeAdapter.java +++ b/bxbot-exchanges/src/main/java/com/gazbert/bxbot/exchanges/CoinbaseProExchangeAdapter.java @@ -2,6 +2,7 @@ * The MIT License (MIT) * * Copyright (c) 2015 Gareth Jon Lynch + * Copyright (c) 2019 David Huertas * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -69,33 +70,29 @@ import org.apache.logging.log4j.Logger; /** - * GDAX exchange has been superseded by Coinbase Pro: https://pro.coinbase.com/ - * - *

DO NOT USE: See https://github.com/gazbert/bxbot/pull/120 - * - *

Exchange Adapter for integrating with the GDAX (formerly Coinbase) exchange. The GDAX API is - * documented here. + * Exchange Adapter for integrating with the CoinbasePro exchange. The CoinbasePro API is documented + * here. * *

DISCLAIMER: This Exchange Adapter is provided as-is; it might have bugs in it and you - * could lose money. Despite running live on GDAX, it has only been unit tested up until the point - * of calling the {@link #sendPublicRequestToExchange(String, Map)} and {@link + * could lose money. Despite running live on COINBASE PRO, it has only been unit tested up until the + * point of calling the {@link #sendPublicRequestToExchange(String, Map)} and {@link * #sendAuthenticatedRequestToExchange(String, String, Map)} methods. Use it at our own risk! * * - *

This adapter only supports the GDAX REST API. The - * design of the API and documentation is excellent. + *

This adapter only supports the CoinbasePro REST + * API. The design of the API and documentation is excellent. * - *

The adapter currently only supports Limit - * Orders. It was originally developed and tested for BTC-GBP market, but it should work for - * BTC-USD. + *

The adapter currently only supports Limit Orders. It was originally + * developed and tested for BTC-GBP market, but it should work for BTC-USD or BTC-EUR. * *

Exchange fees are loaded from the exchange.yaml file on startup; they are not fetched from the - * exchange at runtime as the GDAX REST API does not support this. The fees are used across all - * markets. Make sure you keep an eye on the exchange fees - * and update the config accordingly. + * exchange at runtime as the CoinbasePro REST API does not support this. The fees are used across + * all markets. Make sure you keep an eye on the exchange fees and update the config accordingly. * - *

NOTE: GDAX requires all price values to be limited to 2 decimal places when creating orders. - * This adapter truncates any prices with more than 2 decimal places and rounds using {@link + *

NOTE: CoinbasePro requires all price values to be limited to 2 decimal places when creating + * orders. This adapter truncates any prices with more than 2 decimal places and rounds using {@link * java.math.RoundingMode#HALF_EVEN}, E.g. 250.176 would be sent to the exchange as 250.18. * *

The Exchange Adapter is not thread safe. It expects to be called using a single @@ -106,21 +103,19 @@ * occurs trying to connect to the exchange. A {@link TradingApiException} is thrown for * all other failures. * - * @author gazbert + * @author davidhuertas * @since 1.0 - * @deprecated #120 : GDAX exchange has been superseded by Coinbase Pro: https://pro.coinbase.com/ - - * this adapter will be removed in next release. */ -@Deprecated(forRemoval = true) -public final class GdaxExchangeAdapter extends AbstractExchangeAdapter implements ExchangeAdapter { +public final class CoinbaseProExchangeAdapter extends AbstractExchangeAdapter + implements ExchangeAdapter { private static final Logger LOG = LogManager.getLogger(); - private static final String PUBLIC_API_BASE_URL = "https://api.gdax.com/"; + private static final String PUBLIC_API_BASE_URL = "https://api.pro.coinbase.com/"; private static final String AUTHENTICATED_API_URL = PUBLIC_API_BASE_URL; private static final String UNEXPECTED_ERROR_MSG = - "Unexpected error has occurred in GDAX Exchange Adapter. "; + "Unexpected error has occurred in COINBASE PRO Exchange Adapter. "; private static final String UNEXPECTED_IO_ERROR_MSG = "Failed to connect to Exchange due to unexpected IO error."; @@ -133,9 +128,11 @@ public final class GdaxExchangeAdapter extends AbstractExchangeAdapter implement private static final String BUY_FEE_PROPERTY_NAME = "buy-fee"; private static final String SELL_FEE_PROPERTY_NAME = "sell-fee"; + private static final String SERVER_TIME_BIAS_PROPERTY_NAME = "time-server-bias"; private BigDecimal buyFeePercentage; private BigDecimal sellFeePercentage; + private Long timeServerBias; private String passphrase = ""; private String key = ""; @@ -148,7 +145,7 @@ public final class GdaxExchangeAdapter extends AbstractExchangeAdapter implement @Override public void init(ExchangeConfig config) { - LOG.info(() -> "About to initialise GDAX ExchangeConfig: " + config); + LOG.info(() -> "About to initialise COINBASE PRO ExchangeConfig: " + config); setAuthenticationConfig(config); setNetworkConfig(config); setOtherConfig(config); @@ -158,8 +155,8 @@ public void init(ExchangeConfig config) { } // -------------------------------------------------------------------------- - // GDAX API Calls adapted to the Trading API. - // See https://docs.gdax.com/#api + // COINBASE PRO API Calls adapted to the Trading API. + // See https://docs.pro.coinbase.com/#api // -------------------------------------------------------------------------- @Override @@ -168,7 +165,7 @@ public String createOrder( throws TradingApiException, ExchangeNetworkException { try { /* - * Build Limit Order: https://docs.gdax.com/#place-a-new-order + * Build Limit Order: https://docs.pro.coinbase.com/#place-a-new-order * * stp param optional - (Self-trade prevention flag) defaults to 'dc' Decrease & * Cancel @@ -208,7 +205,8 @@ public String createOrder( LOG.debug(() -> "Create Order response: " + response); if (response.getStatusCode() == HttpURLConnection.HTTP_OK) { - final GdaxOrder createOrderResponse = gson.fromJson(response.getPayload(), GdaxOrder.class); + final CoinbaseProOrder createOrderResponse = + gson.fromJson(response.getPayload(), CoinbaseProOrder.class); if (createOrderResponse != null && (createOrderResponse.id != null && !createOrderResponse.id.isEmpty())) { return createOrderResponse.id; @@ -245,8 +243,8 @@ public boolean cancelOrder(String orderId, String marketIdNotNeeded) LOG.debug(() -> "Cancel Order response: " + response); if (response.getStatusCode() == HttpURLConnection.HTTP_OK) { - // 1 Nov 2017 - GDAX API no longer returns cancelled orderId in array payload; it returns - // [null]... + // 1 Nov 2017 - COINBASE PRO API no longer returns cancelled orderId in array payload; + // it returns [null]... return true; } else { final String errorMsg = "Failed to cancel order on exchange. Details: " + response; @@ -276,9 +274,10 @@ public List getYourOpenOrders(String marketId) LOG.debug(() -> "Open Orders response: " + response); if (response.getStatusCode() == HttpURLConnection.HTTP_OK) { - final GdaxOrder[] gdaxOpenOrders = gson.fromJson(response.getPayload(), GdaxOrder[].class); + final CoinbaseProOrder[] coinbaseProOpenOrders = + gson.fromJson(response.getPayload(), CoinbaseProOrder[].class); final List ordersToReturn = new ArrayList<>(); - for (final GdaxOrder openOrder : gdaxOpenOrders) { + for (final CoinbaseProOrder openOrder : coinbaseProOpenOrders) { if (!marketId.equalsIgnoreCase(openOrder.productId)) { continue; @@ -306,9 +305,9 @@ public List getYourOpenOrders(String marketId) orderType, openOrder.price, openOrder.size.subtract( - openOrder.filledSize), // quantity remaining - not provided by GDAX + openOrder.filledSize), // quantity remaining - not provided by COINBASE PRO openOrder.size, // orig quantity - openOrder.price.multiply(openOrder.size) // total - not provided by GDAX + openOrder.price.multiply(openOrder.size) // total - not provided by COINBASE PRO ); ordersToReturn.add(order); @@ -343,28 +342,28 @@ public MarketOrderBook getMarketOrders(String marketId) LOG.debug(() -> "Market Orders response: " + response); if (response.getStatusCode() == HttpURLConnection.HTTP_OK) { - final GdaxBookWrapper orderBook = - gson.fromJson(response.getPayload(), GdaxBookWrapper.class); + final CoinbaseProBookWrapper orderBook = + gson.fromJson(response.getPayload(), CoinbaseProBookWrapper.class); final List buyOrders = new ArrayList<>(); - for (GdaxMarketOrder gdaxBuyOrder : orderBook.bids) { + for (CoinbaseProMarketOrder coinbaseProBuyOrder : orderBook.bids) { final MarketOrder buyOrder = new MarketOrderImpl( OrderType.BUY, - gdaxBuyOrder.get(0), - gdaxBuyOrder.get(1), - gdaxBuyOrder.get(0).multiply(gdaxBuyOrder.get(1))); + coinbaseProBuyOrder.get(0), + coinbaseProBuyOrder.get(1), + coinbaseProBuyOrder.get(0).multiply(coinbaseProBuyOrder.get(1))); buyOrders.add(buyOrder); } final List sellOrders = new ArrayList<>(); - for (GdaxMarketOrder gdaxSellOrder : orderBook.asks) { + for (CoinbaseProMarketOrder coinbaseProSellOrder : orderBook.asks) { final MarketOrder sellOrder = new MarketOrderImpl( OrderType.SELL, - gdaxSellOrder.get(0), - gdaxSellOrder.get(1), - gdaxSellOrder.get(0).multiply(gdaxSellOrder.get(1))); + coinbaseProSellOrder.get(0), + coinbaseProSellOrder.get(1), + coinbaseProSellOrder.get(0).multiply(coinbaseProSellOrder.get(1))); sellOrders.add(sellOrder); } return new MarketOrderBookImpl(marketId, sellOrders, buyOrders); @@ -394,15 +393,15 @@ public BalanceInfo getBalanceInfo() throws TradingApiException, ExchangeNetworkE LOG.debug(() -> "Balance Info response: " + response); if (response.getStatusCode() == HttpURLConnection.HTTP_OK) { - final GdaxAccount[] gdaxAccounts = - gson.fromJson(response.getPayload(), GdaxAccount[].class); + final CoinbaseProAccount[] coinbaseProAccounts = + gson.fromJson(response.getPayload(), CoinbaseProAccount[].class); final HashMap balancesAvailable = new HashMap<>(); final HashMap balancesOnHold = new HashMap<>(); - for (final GdaxAccount gdaxAccount : gdaxAccounts) { - balancesAvailable.put(gdaxAccount.currency, gdaxAccount.available); - balancesOnHold.put(gdaxAccount.currency, gdaxAccount.hold); + for (final CoinbaseProAccount coinbaseProAccount : coinbaseProAccounts) { + balancesAvailable.put(coinbaseProAccount.currency, coinbaseProAccount.available); + balancesOnHold.put(coinbaseProAccount.currency, coinbaseProAccount.hold); } return new BalanceInfoImpl(balancesAvailable, balancesOnHold); } else { @@ -430,8 +429,9 @@ public BigDecimal getLatestMarketPrice(String marketId) LOG.debug(() -> "Latest Market Price response: " + response); if (response.getStatusCode() == HttpURLConnection.HTTP_OK) { - final GdaxTicker gdaxTicker = gson.fromJson(response.getPayload(), GdaxTicker.class); - return gdaxTicker.price; + final CoinbaseProTicker coinbaseProTicker = + gson.fromJson(response.getPayload(), CoinbaseProTicker.class); + return coinbaseProTicker.price; } else { final String errorMsg = "Failed to get market ticker from exchange. Details: " + response; LOG.error(errorMsg); @@ -448,9 +448,9 @@ public BigDecimal getLatestMarketPrice(String marketId) } /* - * GDAX does not provide API call for fetching % buy fee; it only provides the fee monetary - * value for a given order via e.g. /orders/ API call. We load the % fee statically - * from exchange.yaml file. + * COINBASE PRO does not provide API call for fetching % buy fee; it only provides the fee + * monetary value for a given order via e.g. /orders/ API call. We load the % fee + * statically from exchange.yaml file. */ @Override public BigDecimal getPercentageOfBuyOrderTakenForExchangeFee(String marketId) { @@ -458,9 +458,9 @@ public BigDecimal getPercentageOfBuyOrderTakenForExchangeFee(String marketId) { } /* - * GDAX does not provide API call for fetching % sell fee; it only provides the fee monetary - * value for a given order via e.g. /orders/ API call. We load the % fee statically - * from exchange.yaml file. + * COINBASE PRO does not provide API call for fetching % sell fee; it only provides the fee + * monetary value for a given order via e.g. /orders/ API call. We load the % fee + * statically from exchange.yaml file. */ @Override public BigDecimal getPercentageOfSellOrderTakenForExchangeFee(String marketId) { @@ -469,7 +469,7 @@ public BigDecimal getPercentageOfSellOrderTakenForExchangeFee(String marketId) { @Override public String getImplName() { - return "GDAX REST API v1"; + return "COINBASE PRO REST API v1"; } @Override @@ -481,19 +481,20 @@ public Ticker getTicker(String marketId) throws ExchangeNetworkException, Tradin LOG.debug(() -> "Ticker response: " + tickerResponse); if (tickerResponse.getStatusCode() == HttpURLConnection.HTTP_OK) { - final GdaxTicker gdaxTicker = gson.fromJson(tickerResponse.getPayload(), GdaxTicker.class); + final CoinbaseProTicker coinbaseProTicker = + gson.fromJson(tickerResponse.getPayload(), CoinbaseProTicker.class); final TickerImpl ticker = new TickerImpl( - gdaxTicker.price, - gdaxTicker.bid, - gdaxTicker.ask, + coinbaseProTicker.price, + coinbaseProTicker.bid, + coinbaseProTicker.ask, null, // low, null, // high, null, // open, - gdaxTicker.volume, - null, // vwap - not supplied by GDAX - Date.from(Instant.parse(gdaxTicker.time)).getTime()); + coinbaseProTicker.volume, + null, // vwap - not supplied by COINBASE PRO + Date.from(Instant.parse(coinbaseProTicker.time)).getTime()); // Now we need to call the stats operation to get the 24hr indicators final ExchangeHttpResponse statsResponse = @@ -502,10 +503,11 @@ public Ticker getTicker(String marketId) throws ExchangeNetworkException, Tradin LOG.debug(() -> "Stats response: " + statsResponse); if (statsResponse.getStatusCode() == HttpURLConnection.HTTP_OK) { - final GdaxStats gdaxStats = gson.fromJson(statsResponse.getPayload(), GdaxStats.class); - ticker.setLow(gdaxStats.low); - ticker.setHigh(gdaxStats.high); - ticker.setOpen(gdaxStats.open); + final CoinbaseProStats coinbaseProStats = + gson.fromJson(statsResponse.getPayload(), CoinbaseProStats.class); + ticker.setLow(coinbaseProStats.low); + ticker.setHigh(coinbaseProStats.high); + ticker.setOpen(coinbaseProStats.open); } else { final String errorMsg = "Failed to get stats from exchange. Details: " + statsResponse; LOG.error(errorMsg); @@ -532,16 +534,16 @@ public Ticker getTicker(String marketId) throws ExchangeNetworkException, Tradin // -------------------------------------------------------------------------- // GSON classes for JSON responses. - // See https://docs.gdax.com/#api + // See https://docs.pro.coinbase.com/#api // -------------------------------------------------------------------------- /** - * GSON class for GDAX '/orders' API call response. + * GSON class for COINBASE PRO '/orders' API call response. * *

There are other critters in here different to what is spec'd: - * https://docs.gdax.com/#list-orders + * https://docs.pro.coinbase.com/#list-orders */ - private static class GdaxOrder { + private static class CoinbaseProOrder { String id; BigDecimal price; @@ -593,12 +595,12 @@ public String toString() { } } - /** GSON class for GDAX '/products/{marketId}/book' API call response. */ - private static class GdaxBookWrapper { + /** GSON class for COINBASE PRO '/products/{marketId}/book' API call response. */ + private static class CoinbaseProBookWrapper { long sequence; - List bids; - List asks; + List bids; + List asks; @Override public String toString() { @@ -614,13 +616,13 @@ public String toString() { * GSON class for holding Market Orders. First element in array is price, second element is * amount, third is number of orders. */ - private static class GdaxMarketOrder extends ArrayList { + private static class CoinbaseProMarketOrder extends ArrayList { private static final long serialVersionUID = -4919711220797077759L; } - /** GSON class for GDAX '/products/{marketId}/ticker' API call response. */ - private static class GdaxTicker { + /** GSON class for COINBASE PRO '/products/{marketId}/ticker' API call response. */ + private static class CoinbaseProTicker { @SerializedName("trade_id") long tradeId; @@ -646,8 +648,8 @@ public String toString() { } } - /** GSON class for GDAX '/products/<product-id>/stats' API call response. */ - private static class GdaxStats { + /** GSON class for COINBASE PRO '/products/<product-id>/stats' API call response. */ + private static class CoinbaseProStats { BigDecimal open; BigDecimal high; @@ -671,8 +673,8 @@ public String toString() { } } - /** GSON class for GDAX '/accounts' API call response. */ - private static class GdaxAccount { + /** GSON class for COINBASE PRO '/accounts' API call response. */ + private static class CoinbaseProAccount { String id; String currency; @@ -740,43 +742,44 @@ private ExchangeHttpResponse sendPublicRequestToExchange( } /* - * Makes an authenticated API call to the GDAX exchange. - * - * The GDAX authentication process is complex, but well documented: https://docs.gdax.com/#creating-a-request - - * All REST requests must contain the following headers: - * - * CB-ACCESS-KEY The api key as a string. - * CB-ACCESS-SIGN The base64-encoded signature (see Signing a Message). - * CB-ACCESS-TIMESTAMP A timestamp for your request. - * CB-ACCESS-PASSPHRASE The passphrase you specified when creating the API key. - * - * The CB-ACCESS-TIMESTAMP header MUST be number of seconds since Unix Epoch in UTC. - * Decimal values are allowed. - * - * Your timestamp must be within 30 seconds of the api service time or your request will be - * considered expired and rejected. We recommend using the time endpoint to query for the API - * server time if you believe there many be time skew between your server and the API servers. - * - * All request bodies should have content type application/json and be valid JSON. - * - * The CB-ACCESS-SIGN header is generated by creating a sha256 HMAC using the base64-decoded - * secret key on the prehash string: - * - * timestamp + method + requestPath + body (where + represents string concatenation) - * - * and base64-encode the output. - * The timestamp value is the same as the CB-ACCESS-TIMESTAMP header. - * - * The body is the request body string or omitted if there is no request body - * (typically for GET requests). - * - * The method should be UPPER CASE. - * - * Remember to first base64-decode the alphanumeric secret string (resulting in 64 bytes) before - * using it as the key for HMAC. Also, base64-encode the digest output before sending in the - * header. - */ + * Makes an authenticated API call to the COINBASE PRO exchange. + * + * The COINBASE PRO authentication process is complex, but well documented: + * https://docs.pro.coinbase.com/#creating-a-request + * + * All REST requests must contain the following headers: + * + * CB-ACCESS-KEY The api key as a string. + * CB-ACCESS-SIGN The base64-encoded signature (see Signing a Message). + * CB-ACCESS-TIMESTAMP A timestamp for your request. + * CB-ACCESS-PASSPHRASE The passphrase you specified when creating the API key. + * + * The CB-ACCESS-TIMESTAMP header MUST be number of seconds since Unix Epoch in UTC. + * Decimal values are allowed. + * + * Your timestamp must be within 30 seconds of the api service time or your request will be + * considered expired and rejected. We recommend using the time endpoint to query for the API + * server time if you believe there many be time skew between your server and the API servers. + * + * All request bodies should have content type application/json and be valid JSON. + * + * The CB-ACCESS-SIGN header is generated by creating a sha256 HMAC using the base64-decoded + * secret key on the prehash string: + * + * timestamp + method + requestPath + body (where + represents string concatenation) + * + * and base64-encode the output. + * The timestamp value is the same as the CB-ACCESS-TIMESTAMP header. + * + * The body is the request body string or omitted if there is no request body + * (typically for GET requests). + * + * The method should be UPPER CASE. + * + * Remember to first base64-decode the alphanumeric secret string (resulting in 64 bytes) before + * using it as the key for HMAC. Also, base64-encode the digest output before sending in the + * header. + */ private ExchangeHttpResponse sendAuthenticatedRequestToExchange( String httpMethod, String apiMethod, Map params) throws ExchangeNetworkException, TradingApiException { @@ -793,9 +796,6 @@ private ExchangeHttpResponse sendAuthenticatedRequestToExchange( params = createRequestParamMap(); } - // Get UNIX time in secs - final String timestamp = Long.toString(System.currentTimeMillis() / 1000); - // Build the request final String invocationUrl; String requestBody = ""; @@ -840,7 +840,12 @@ private ExchangeHttpResponse sendAuthenticatedRequestToExchange( "Don't know how to build secure [" + httpMethod + "] request!"); } - // Build the signature string + // Get UNIX EPOCH in secs and add the time server bias + final long timeServer = Instant.now().getEpochSecond() + timeServerBias; + final String timestamp = Long.toString(timeServer); + LOG.debug(() -> "Server UNIX EPOCH in seconds: " + timestamp); + + // Build the signature string: timestamp + method + requestPath + body final String signatureBuilder = timestamp + httpMethod.toUpperCase() + "/" + apiMethod + requestBody; @@ -875,7 +880,7 @@ private ExchangeHttpResponse sendAuthenticatedRequestToExchange( */ private void initSecureMessageLayer() { try { - // GDAX secret is in Base64 so we must decode it first. + // COINBASE PRO secret is in Base64 so we must decode it first. final byte[] decodedBase64Secret = DatatypeConverter.parseBase64Binary(secret); final SecretKeySpec keyspec = new SecretKeySpec(decodedBase64Secret, "HmacSHA256"); @@ -916,6 +921,11 @@ private void setOtherConfig(ExchangeConfig exchangeConfig) { sellFeePercentage = new BigDecimal(sellFeeInConfig).divide(new BigDecimal("100"), 8, RoundingMode.HALF_UP); LOG.info(() -> "Sell fee % in BigDecimal format: " + sellFeePercentage); + + final String serverTimeBiasInConfig = + getOtherConfigItem(otherConfig, SERVER_TIME_BIAS_PROPERTY_NAME); + timeServerBias = Long.parseLong(serverTimeBiasInConfig); + LOG.info(() -> "Time server bias in long format: " + timeServerBias); } // -------------------------------------------------------------------------- diff --git a/bxbot-exchanges/src/main/java/com/gazbert/bxbot/exchanges/OkCoinExchangeAdapter.java b/bxbot-exchanges/src/main/java/com/gazbert/bxbot/exchanges/OkCoinExchangeAdapter.java deleted file mode 100644 index df79af42b..000000000 --- a/bxbot-exchanges/src/main/java/com/gazbert/bxbot/exchanges/OkCoinExchangeAdapter.java +++ /dev/null @@ -1,899 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Gareth Jon Lynch - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.gazbert.bxbot.exchanges; - -import com.gazbert.bxbot.exchange.api.AuthenticationConfig; -import com.gazbert.bxbot.exchange.api.ExchangeAdapter; -import com.gazbert.bxbot.exchange.api.ExchangeConfig; -import com.gazbert.bxbot.exchange.api.OtherConfig; -import com.gazbert.bxbot.exchanges.trading.api.impl.BalanceInfoImpl; -import com.gazbert.bxbot.exchanges.trading.api.impl.MarketOrderBookImpl; -import com.gazbert.bxbot.exchanges.trading.api.impl.MarketOrderImpl; -import com.gazbert.bxbot.exchanges.trading.api.impl.OpenOrderImpl; -import com.gazbert.bxbot.exchanges.trading.api.impl.TickerImpl; -import com.gazbert.bxbot.trading.api.BalanceInfo; -import com.gazbert.bxbot.trading.api.ExchangeNetworkException; -import com.gazbert.bxbot.trading.api.MarketOrder; -import com.gazbert.bxbot.trading.api.MarketOrderBook; -import com.gazbert.bxbot.trading.api.OpenOrder; -import com.gazbert.bxbot.trading.api.OrderType; -import com.gazbert.bxbot.trading.api.Ticker; -import com.gazbert.bxbot.trading.api.TradingApi; -import com.gazbert.bxbot.trading.api.TradingApiException; -import com.google.common.base.MoreObjects; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.annotations.SerializedName; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -/** - * DO NOT USE: See https://github.com/gazbert/bxbot/issues/122 - * - *

Exchange Adapter for integrating with the OKCoin exchange. The OKCoin API is documented here. - * - *

DISCLAIMER: This Exchange Adapter is provided as-is; it might have bugs in it and you - * could lose money. Despite running live on OKCoin, it has only been unit tested up until the point - * of calling the {@link #sendPublicRequestToExchange(String, Map)} and {@link - * #sendAuthenticatedRequestToExchange(String, Map)} methods. Use it at our own risk! - * - *

It only supports the REST implementation of the Spot Trading API. - * - *

The exchange % buy and sell fees are currently loaded statically from the exchange.yaml file - * on startup; they are not fetched from the exchange at runtime as the OKCoin API does not support - * this - it only provides the fee monetary value for a given order id via the order_fee.do API - * call. The fees are used across all markets. Make sure you keep an eye on the exchange fees and update the config accordingly. - * - *

The Exchange Adapter is not thread safe. It expects to be called using a single - * thread in order to preserve trade execution order. The {@link URLConnection} achieves this by - * blocking/waiting on the input stream (response) for each API call. - * - *

The {@link TradingApi} calls will throw a {@link ExchangeNetworkException} if a network error - * occurs trying to connect to the exchange. A {@link TradingApiException} is thrown for - * all other failures. - * - * @author gazbert - * @deprecated #120 : The OKCoin V1 API is now deprecated and no longer works - adapter needs - * updating to use V3 API. - */ -@Deprecated(forRemoval = true) -public final class OkCoinExchangeAdapter extends AbstractExchangeAdapter - implements ExchangeAdapter { - - private static final Logger LOG = LogManager.getLogger(); - - private static final String OKCOIN_API_VERSION = "v1"; - private static final String PUBLIC_API_BASE_URL = - "https://www.okcoin.com/api/" + OKCOIN_API_VERSION + "/"; - private static final String AUTHENTICATED_API_URL = PUBLIC_API_BASE_URL; - - private static final String UNEXPECTED_ERROR_MSG = - "Unexpected error has occurred in OKCoin Exchange Adapter. "; - private static final String UNEXPECTED_IO_ERROR_MSG = - "Failed to connect to Exchange due to unexpected IO error."; - - private static final String SYMBOL = "symbol"; - private static final String ORDER_ID = "orderId"; - - private static final String KEY_PROPERTY_NAME = "key"; - private static final String SECRET_PROPERTY_NAME = "secret"; - - private static final String BUY_FEE_PROPERTY_NAME = "buy-fee"; - private static final String SELL_FEE_PROPERTY_NAME = "sell-fee"; - - private BigDecimal buyFeePercentage; - private BigDecimal sellFeePercentage; - - private String key = ""; - private String secret = ""; - - private MessageDigest messageDigest; - private boolean initializedSecureMessagingLayer = false; - - private Gson gson; - - @Override - public void init(ExchangeConfig config) { - LOG.info(() -> "About to initialise OKCoin ExchangeConfig: " + config); - setAuthenticationConfig(config); - setNetworkConfig(config); - setOtherConfig(config); - - initSecureMessageLayer(); - initGson(); - } - - // -------------------------------------------------------------------------- - // OKCoin REST Spot Trading API Calls adapted to the Trading API. - // See https://www.okcoin.com/about/rest_getStarted.do - // -------------------------------------------------------------------------- - - @Override - public String createOrder( - String marketId, OrderType orderType, BigDecimal quantity, BigDecimal price) - throws TradingApiException, ExchangeNetworkException { - - try { - final Map params = createRequestParamMap(); - params.put(SYMBOL, marketId); - - if (orderType == OrderType.BUY) { - params.put("type", "buy"); - } else if (orderType == OrderType.SELL) { - params.put("type", "sell"); - } else { - final String errorMsg = - "Invalid order type: " - + orderType - + " - Can only be " - + OrderType.BUY.getStringValue() - + " or " - + OrderType.SELL.getStringValue(); - LOG.error(errorMsg); - throw new IllegalArgumentException(errorMsg); - } - - params.put("price", new DecimalFormat("#.########", getDecimalFormatSymbols()).format(price)); - - // note we need to limit amount to 8 decimal places else exchange will barf - params.put( - "amount", new DecimalFormat("#.########", getDecimalFormatSymbols()).format(quantity)); - - final ExchangeHttpResponse response = sendAuthenticatedRequestToExchange("trade.do", params); - LOG.debug(() -> "Create Order response: " + response); - - final OkCoinTradeResponse createOrderResponse = - gson.fromJson(response.getPayload(), OkCoinTradeResponse.class); - if (createOrderResponse.result) { - return Long.toString(createOrderResponse.orderId); - } else { - final String errorMsg = "Failed to place order on exchange. Error response: " + response; - LOG.error(errorMsg); - throw new TradingApiException(errorMsg); - } - - } catch (ExchangeNetworkException | TradingApiException e) { - throw e; - - } catch (Exception e) { - LOG.error(UNEXPECTED_ERROR_MSG, e); - throw new TradingApiException(UNEXPECTED_ERROR_MSG, e); - } - } - - @Override - public boolean cancelOrder(String orderId, String marketId) - throws TradingApiException, ExchangeNetworkException { - try { - final Map params = createRequestParamMap(); - params.put("order_id", orderId); - params.put(SYMBOL, marketId); - - final ExchangeHttpResponse response = - sendAuthenticatedRequestToExchange("cancel_order.do", params); - LOG.debug(() -> "Cancel Order response: " + response); - - final OkCoinCancelOrderResponse cancelOrderResponse = - gson.fromJson(response.getPayload(), OkCoinCancelOrderResponse.class); - if (cancelOrderResponse.result) { - return true; - } else { - final String errorMsg = "Failed to cancel order on exchange. Error response: " + response; - LOG.error(errorMsg); - return false; - } - - } catch (ExchangeNetworkException | TradingApiException e) { - throw e; - } catch (Exception e) { - LOG.error(UNEXPECTED_ERROR_MSG, e); - throw new TradingApiException(UNEXPECTED_ERROR_MSG, e); - } - } - - @Override - public List getYourOpenOrders(String marketId) - throws TradingApiException, ExchangeNetworkException { - try { - final Map params = createRequestParamMap(); - params.put(SYMBOL, marketId); - params.put("order_id", "-1"); // -1 means bring back all the orders - - final ExchangeHttpResponse response = - sendAuthenticatedRequestToExchange("order_info.do", params); - LOG.debug(() -> "Open Orders response: " + response); - - final OkCoinOrderInfoWrapper orderInfoWrapper = - gson.fromJson(response.getPayload(), OkCoinOrderInfoWrapper.class); - if (orderInfoWrapper.result) { - - final List ordersToReturn = new ArrayList<>(); - for (final OkCoinOpenOrder openOrder : orderInfoWrapper.orders) { - OrderType orderType; - switch (openOrder.type) { - case "buy": - orderType = OrderType.BUY; - break; - case "sell": - orderType = OrderType.SELL; - break; - default: - throw new TradingApiException( - "Unrecognised order type received in getYourOpenOrders(). Value: " - + openOrder.type); - } - - final OpenOrder order = - new OpenOrderImpl( - Long.toString(openOrder.orderId), - new Date(openOrder.createDate), - marketId, - orderType, - openOrder.price, - openOrder.amount, - null, // orig_quantity - not provided by OKCoin :-( - openOrder.price.multiply(openOrder.amount) // total - not provided by OKCoin :-( - ); - - ordersToReturn.add(order); - } - return ordersToReturn; - - } else { - final String errorMsg = - "Failed to get Open Order Info from exchange. Error response: " + response; - LOG.error(errorMsg); - throw new TradingApiException(errorMsg); - } - - } catch (ExchangeNetworkException | TradingApiException e) { - throw e; - - } catch (Exception e) { - LOG.error(UNEXPECTED_ERROR_MSG, e); - throw new TradingApiException(UNEXPECTED_ERROR_MSG, e); - } - } - - @Override - public MarketOrderBook getMarketOrders(String marketId) - throws TradingApiException, ExchangeNetworkException { - try { - final Map params = createRequestParamMap(); - params.put(SYMBOL, marketId); - - final ExchangeHttpResponse response = sendPublicRequestToExchange("depth.do", params); - LOG.debug(() -> "Market Orders response: " + response); - - final OkCoinDepthWrapper orderBook = - gson.fromJson(response.getPayload(), OkCoinDepthWrapper.class); - - final List buyOrders = new ArrayList<>(); - for (OkCoinMarketOrder okCoinBuyOrder : orderBook.bids) { - final MarketOrder buyOrder = - new MarketOrderImpl( - OrderType.BUY, - okCoinBuyOrder.get(0), - okCoinBuyOrder.get(1), - okCoinBuyOrder.get(0).multiply(okCoinBuyOrder.get(1))); - buyOrders.add(buyOrder); - } - - final List sellOrders = new ArrayList<>(); - for (OkCoinMarketOrder okCoinSellOrder : orderBook.asks) { - final MarketOrder sellOrder = - new MarketOrderImpl( - OrderType.SELL, - okCoinSellOrder.get(0), - okCoinSellOrder.get(1), - okCoinSellOrder.get(0).multiply(okCoinSellOrder.get(1))); - sellOrders.add(sellOrder); - } - - // For some reason, OKCoin sorts ask orders in descending order instead of ascending. - // We need to re-order price ascending - lowest ASK price will be first in list. - sellOrders.sort( - (thisOrder, thatOrder) -> - Integer.compare(thisOrder.getPrice().compareTo(thatOrder.getPrice()), 0)); - return new MarketOrderBookImpl(marketId, sellOrders, buyOrders); - - } catch (ExchangeNetworkException | TradingApiException e) { - throw e; - - } catch (Exception e) { - LOG.error(UNEXPECTED_ERROR_MSG, e); - throw new TradingApiException(UNEXPECTED_ERROR_MSG, e); - } - } - - @Override - public BigDecimal getLatestMarketPrice(String marketId) - throws ExchangeNetworkException, TradingApiException { - try { - final Map params = createRequestParamMap(); - params.put(SYMBOL, marketId); - - final ExchangeHttpResponse response = sendPublicRequestToExchange("ticker.do", params); - LOG.debug(() -> "Latest Market Price response: " + response); - - final OkCoinTickerWrapper tickerWrapper = - gson.fromJson(response.getPayload(), OkCoinTickerWrapper.class); - return tickerWrapper.ticker.last; - - } catch (ExchangeNetworkException | TradingApiException e) { - throw e; - - } catch (Exception e) { - LOG.error(UNEXPECTED_ERROR_MSG, e); - throw new TradingApiException(UNEXPECTED_ERROR_MSG, e); - } - } - - @Override - public BalanceInfo getBalanceInfo() throws TradingApiException, ExchangeNetworkException { - try { - final ExchangeHttpResponse response = sendAuthenticatedRequestToExchange("userinfo.do", null); - LOG.debug(() -> "Balance Info response: " + response); - - final OkCoinUserInfoWrapper userInfoWrapper = - gson.fromJson(response.getPayload(), OkCoinUserInfoWrapper.class); - if (userInfoWrapper.result) { - final Map balancesAvailable = new HashMap<>(); - for (final Map.Entry balance : - userInfoWrapper.info.funds.free.entrySet()) { - balancesAvailable.put(balance.getKey().toUpperCase(), balance.getValue()); - } - - final Map balancesOnOrder = new HashMap<>(); - for (final Map.Entry balance : - userInfoWrapper.info.funds.freezed.entrySet()) { - balancesOnOrder.put(balance.getKey().toUpperCase(), balance.getValue()); - } - - return new BalanceInfoImpl(balancesAvailable, balancesOnOrder); - - } else { - final String errorMsg = - "Failed to get Balance Info from exchange. Error response: " + response; - LOG.error(errorMsg); - throw new TradingApiException(errorMsg); - } - - } catch (ExchangeNetworkException | TradingApiException e) { - throw e; - - } catch (Exception e) { - LOG.error(UNEXPECTED_ERROR_MSG, e); - throw new TradingApiException(UNEXPECTED_ERROR_MSG, e); - } - } - - /* - * OKCoin does not provide API call for fetching % buy fee; it only provides the fee monetary - * value for a given order via order_fee.do API call. We load the % fee statically from - * exchange.yaml file. - */ - @Override - public BigDecimal getPercentageOfBuyOrderTakenForExchangeFee(String marketId) { - return buyFeePercentage; - } - - /* - * OKCoin does not provide API call for fetching % sell fee; it only provides the fee monetary - * value for a given order via order_fee.do API call. We load the % fee statically from - * exchange.yaml file. - */ - @Override - public BigDecimal getPercentageOfSellOrderTakenForExchangeFee(String marketId) { - return sellFeePercentage; - } - - @Override - public String getImplName() { - return "OKCoin REST Spot Trading API v1"; - } - - @Override - public Ticker getTicker(String marketId) throws ExchangeNetworkException, TradingApiException { - try { - final Map params = createRequestParamMap(); - params.put(SYMBOL, marketId); - - final ExchangeHttpResponse response = sendPublicRequestToExchange("ticker.do", params); - LOG.debug(() -> "Latest Market Price response: " + response); - - final OkCoinTickerWrapper tickerWrapper = - gson.fromJson(response.getPayload(), OkCoinTickerWrapper.class); - return new TickerImpl( - tickerWrapper.ticker.last, - tickerWrapper.ticker.buy, - tickerWrapper.ticker.sell, - tickerWrapper.ticker.low, - tickerWrapper.ticker.high, - null, // open not supplied by OKCoin - tickerWrapper.ticker.vol, - null, // vwap not supplied by OKCoin - Long.valueOf(tickerWrapper.date)); - - } catch (ExchangeNetworkException | TradingApiException e) { - throw e; - - } catch (Exception e) { - LOG.error(UNEXPECTED_ERROR_MSG, e); - throw new TradingApiException(UNEXPECTED_ERROR_MSG, e); - } - } - - // -------------------------------------------------------------------------- - // GSON classes for JSON responses. - // See https://www.okcoin.com/about/rest_getStarted.do - // -------------------------------------------------------------------------- - - /** GSON class for wrapping cancel_order.do response. */ - public static class OkCoinCancelOrderResponse extends OkCoinMessageBase { - - @SerializedName("order_id") - long orderId; - - @Override - public String toString() { - return MoreObjects.toStringHelper(this).add(ORDER_ID, orderId).toString(); - } - } - - /** GSON class for wrapping trade.do response. */ - public static class OkCoinTradeResponse extends OkCoinMessageBase { - - @SerializedName("order_id") - long orderId; - - @Override - public String toString() { - return MoreObjects.toStringHelper(this).add(ORDER_ID, orderId).toString(); - } - } - - /** GSON class for wrapping order_info.do response. */ - private static class OkCoinOrderInfoWrapper extends OkCoinMessageBase { - - List orders; - - @Override - public String toString() { - return MoreObjects.toStringHelper(this).add("orders", orders).toString(); - } - } - - /** GSON class for holding your open orders info from order_info.do API call. */ - private static class OkCoinOpenOrder { - - BigDecimal amount; - - @SerializedName("avg_price") - BigDecimal avgPrice; - - @SerializedName("create_date") - long createDate; - - BigDecimal dealAmount; - - @SerializedName("order_id") - long orderId; - - @SerializedName("orders_id") - long ordersId; // deprecated - - BigDecimal price; - - /* -1 = cancelled, 0 = unfilled, 1 = partially filled, 2 = fully filled, - * 4 = cancel request in process - */ - int status; - - String symbol; // e.g. 'btc_usd' - String type; // 'sell' or 'buy' - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("amount", amount) - .add("avgPrice", avgPrice) - .add("createDate", createDate) - .add("dealAmount", dealAmount) - .add(ORDER_ID, orderId) - .add("ordersId", ordersId) - .add("price", price) - .add("status", status) - .add(SYMBOL, symbol) - .add("type", type) - .toString(); - } - } - - /** GSON class for wrapping depth.do response. */ - private static class OkCoinDepthWrapper { - - List asks; - List bids; - - @Override - public String toString() { - return MoreObjects.toStringHelper(this).add("asks", asks).add("bids", bids).toString(); - } - } - - /** - * GSON class for holding Market Orders. First element in array is price, second element is - * amount. - */ - private static class OkCoinMarketOrder extends ArrayList { - - private static final long serialVersionUID = -4919711260747077759L; - } - - /** GSON class for wrapping userinfo.do response. */ - private static class OkCoinUserInfoWrapper extends OkCoinMessageBase { - - OkCoinUserInfo info; - - @Override - public String toString() { - return MoreObjects.toStringHelper(this).add("info", info).toString(); - } - } - - /** GSON class for holding funds in userinfo.do response. */ - private static class OkCoinUserInfo { - - OkCoinFundsInfo funds; - - @Override - public String toString() { - return MoreObjects.toStringHelper(this).add("funds", funds).toString(); - } - } - - /** GSON class for holding funds info from userinfo.do response. */ - private static class OkCoinFundsInfo { - - OkCoinAssetInfo asset; - OkCoinBalances free; - OkCoinBalances freezed; - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("asset", asset) - .add("free", free) - .add("freezed", freezed) - .toString(); - } - } - - /** GSON class for holding asset info from userinfo.do response. */ - private static class OkCoinAssetInfo { - - BigDecimal net; - BigDecimal total; - - @Override - public String toString() { - return MoreObjects.toStringHelper(this).add("net", net).add("total", total).toString(); - } - } - - /** GSON class for holding wallet balances - basically a GSON enabled map. */ - private static class OkCoinBalances extends HashMap { - - private static final long serialVersionUID = -4919711060747077759L; - } - - /** GSON class for wrapping OKCoin ticker.do response. */ - private static class OkCoinTickerWrapper { - - String date; - OkCoinTicker ticker; - - @Override - public String toString() { - return MoreObjects.toStringHelper(this).add("date", date).add("ticker", ticker).toString(); - } - } - - /** GSON class for a OkCoin ticker response. */ - private static class OkCoinTicker { - - BigDecimal buy; - BigDecimal high; - BigDecimal last; - BigDecimal low; - BigDecimal sell; - BigDecimal vol; - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("buy", buy) - .add("high", high) - .add("last", last) - .add("low", low) - .add("sell", sell) - .add("vol", vol) - .toString(); - } - } - - /** GSON base class for API call requests and responses. */ - private static class OkCoinMessageBase { - - @SerializedName("error_code") - int errorCode; // will be 0 if not an error response - - boolean result; // will be JSON boolean value in response: true or false - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("errorCode", errorCode) - .add("result", result) - .toString(); - } - } - - // -------------------------------------------------------------------------- - // Transport layer methods - // -------------------------------------------------------------------------- - - private ExchangeHttpResponse sendPublicRequestToExchange( - String apiMethod, Map params) - throws ExchangeNetworkException, TradingApiException { - - if (params == null) { - params = createRequestParamMap(); // no params, so empty query string - } - - final Map requestHeaders = createHeaderParamMap(); - - try { - final StringBuilder queryString = new StringBuilder(); - if (!params.isEmpty()) { - queryString.append("?"); - for (final Map.Entry param : params.entrySet()) { - if (queryString.length() > 1) { - queryString.append("&"); - } - queryString.append(param.getKey()); - queryString.append("="); - queryString.append(URLEncoder.encode(param.getValue(), StandardCharsets.UTF_8)); - } - - requestHeaders.put("Content-Type", "application/x-www-form-urlencoded"); - } - - final URL url = new URL(PUBLIC_API_BASE_URL + apiMethod + queryString); - return makeNetworkRequest(url, "GET", null, requestHeaders); - - } catch (MalformedURLException e) { - final String errorMsg = UNEXPECTED_IO_ERROR_MSG; - LOG.error(errorMsg, e); - throw new TradingApiException(errorMsg, e); - } - } - - /* - * Makes an authenticated API call to the OkCoin exchange. - * - * A tricky one to build! - * - * POST payload generation: - * - * All parameters except for "sign" must be signed. The parameters must be re-ordered according - * to the initials of the parameter name, alphabetically. For example, if the request parameters - * are string[] parameters= - * - * {"api_key=c821db84-6fbd-11e4-a9e3-c86000d26d7c","symbol=btc_usd","type=buy","price=680", - * "amount=1.0"}; - * - * The result string is: - * amount=1.0&api_key=c821db84-6fbd-11e4-a9e3-c86000d26d7c&price=680&symbol=btc_usd&type=buy - * - * Signature creation: - * - * 'secretKey' is required to generate MD5 signature. Add the 'secret_Key' to the above string to - * generate the final string to be signed, such as: - * - * amount=1.0&api_key=c821db84-6fbd-11e4-a9e3-c86000d26d7c&price=680&symbol=btc_usd - * &type=buy&secret_key=secretKey - * - * Note: '&secret_key=secretKey' is a must. - * Use 32 bit MD5 encryption function to sign the string. Pass the encrypted string to 'sign' - * parameter. Letters of the encrypted string must be in upper case. - */ - private ExchangeHttpResponse sendAuthenticatedRequestToExchange( - String apiMethod, Map params) - throws ExchangeNetworkException, TradingApiException { - - if (!initializedSecureMessagingLayer) { - final String errorMsg = "Message security layer has not been initialized."; - LOG.error(errorMsg); - throw new IllegalStateException(errorMsg); - } - - try { - if (params == null) { - params = createRequestParamMap(); - } - - // we always need the API key - params.put("api_key", key); - - String sortedQueryString = createAlphabeticallySortedQueryString(params); - if (LOG.isDebugEnabled()) { - LOG.debug("Sorted Query String without secret: {}", sortedQueryString); - } - - // Add secret key to Query String - sortedQueryString += "&secret_key=" + secret; - - final String signature = createMd5HashAndReturnAsUpperCaseString(sortedQueryString); - params.put("sign", signature); - - // Build the payload with all the param args in it - final StringBuilder payload = new StringBuilder(); - for (final Map.Entry param : params.entrySet()) { - if (payload.length() > 0) { - payload.append("&"); - } - payload.append(param.getKey()); - payload.append("="); - payload.append(URLEncoder.encode(param.getValue(), StandardCharsets.UTF_8)); - } - LOG.debug(() -> "Using following URL encoded POST payload for API call: " + payload); - - final Map requestHeaders = createHeaderParamMap(); - requestHeaders.put("Content-Type", "application/x-www-form-urlencoded"); - - final URL url = new URL(AUTHENTICATED_API_URL + apiMethod); - return makeNetworkRequest(url, "POST", payload.toString(), requestHeaders); - - } catch (MalformedURLException e) { - final String errorMsg = UNEXPECTED_IO_ERROR_MSG; - LOG.error(errorMsg, e); - throw new TradingApiException(errorMsg, e); - } - } - - private String createMd5HashAndReturnAsUpperCaseString(String stringToHash) { - final char[] hexDigits = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' - }; - if (stringToHash == null || stringToHash.isEmpty()) { - return ""; - } - - messageDigest.update(stringToHash.getBytes(StandardCharsets.UTF_8)); - final byte[] md5HashInBytes = messageDigest.digest(); - - final StringBuilder md5HashAsUpperCaseString = new StringBuilder(); - for (final byte md5HashByte : md5HashInBytes) { - md5HashAsUpperCaseString - .append(hexDigits[(md5HashByte & 0xf0) >> 4]) - .append(hexDigits[md5HashByte & 0xf]); - } - return md5HashAsUpperCaseString.toString(); - } - - /* - * Initialises the secure messaging layer. - * Sets up the MAC to safeguard the data we send to the exchange. - * Used to encrypt the hash of the entire message with the private key to ensure message - * integrity. We fail hard n fast if any of this stuff blows. - */ - private void initSecureMessageLayer() { - try { - messageDigest = MessageDigest.getInstance("MD5"); - initializedSecureMessagingLayer = true; - } catch (NoSuchAlgorithmException e) { - final String errorMsg = - "Failed to setup MessageDigest for secure message layer. Details: " + e.getMessage(); - LOG.error(errorMsg, e); - throw new IllegalStateException(errorMsg, e); - } - } - - // -------------------------------------------------------------------------- - // Config methods - // -------------------------------------------------------------------------- - - private void setAuthenticationConfig(ExchangeConfig exchangeConfig) { - final AuthenticationConfig authenticationConfig = getAuthenticationConfig(exchangeConfig); - key = getAuthenticationConfigItem(authenticationConfig, KEY_PROPERTY_NAME); - secret = getAuthenticationConfigItem(authenticationConfig, SECRET_PROPERTY_NAME); - } - - private void setOtherConfig(ExchangeConfig exchangeConfig) { - final OtherConfig otherConfig = getOtherConfig(exchangeConfig); - - final String buyFeeInConfig = getOtherConfigItem(otherConfig, BUY_FEE_PROPERTY_NAME); - buyFeePercentage = - new BigDecimal(buyFeeInConfig).divide(new BigDecimal("100"), 8, RoundingMode.HALF_UP); - LOG.info(() -> "Buy fee % in BigDecimal format: " + buyFeePercentage); - - final String sellFeeInConfig = getOtherConfigItem(otherConfig, SELL_FEE_PROPERTY_NAME); - sellFeePercentage = - new BigDecimal(sellFeeInConfig).divide(new BigDecimal("100"), 8, RoundingMode.HALF_UP); - LOG.info(() -> "Sell fee % in BigDecimal format: " + sellFeePercentage); - } - - // -------------------------------------------------------------------------- - // Util methods - // -------------------------------------------------------------------------- - - /** Initialises the GSON layer. */ - private void initGson() { - final GsonBuilder gsonBuilder = new GsonBuilder(); - gson = gsonBuilder.create(); - } - - /* - * Hack for unit-testing map params passed to transport layer. - */ - private Map createRequestParamMap() { - return new HashMap<>(); - } - - /* - * Hack for unit-testing header params passed to transport layer. - */ - private Map createHeaderParamMap() { - return new HashMap<>(); - } - - /* - * Hack for unit-testing transport layer. - */ - private ExchangeHttpResponse makeNetworkRequest( - URL url, String httpMethod, String postData, Map requestHeaders) - throws TradingApiException, ExchangeNetworkException { - return super.sendNetworkRequest(url, httpMethod, postData, requestHeaders); - } -} diff --git a/bxbot-exchanges/src/test/exchange-data/gdax/accounts.json b/bxbot-exchanges/src/test/exchange-data/coinbasepro/accounts.json similarity index 100% rename from bxbot-exchanges/src/test/exchange-data/gdax/accounts.json rename to bxbot-exchanges/src/test/exchange-data/coinbasepro/accounts.json diff --git a/bxbot-exchanges/src/test/exchange-data/gdax/book.json b/bxbot-exchanges/src/test/exchange-data/coinbasepro/book.json similarity index 100% rename from bxbot-exchanges/src/test/exchange-data/gdax/book.json rename to bxbot-exchanges/src/test/exchange-data/coinbasepro/book.json diff --git a/bxbot-exchanges/src/test/exchange-data/gdax/cancel.json b/bxbot-exchanges/src/test/exchange-data/coinbasepro/cancel.json similarity index 100% rename from bxbot-exchanges/src/test/exchange-data/gdax/cancel.json rename to bxbot-exchanges/src/test/exchange-data/coinbasepro/cancel.json diff --git a/bxbot-exchanges/src/test/exchange-data/gdax/new_buy_order.json b/bxbot-exchanges/src/test/exchange-data/coinbasepro/new_buy_order.json similarity index 100% rename from bxbot-exchanges/src/test/exchange-data/gdax/new_buy_order.json rename to bxbot-exchanges/src/test/exchange-data/coinbasepro/new_buy_order.json diff --git a/bxbot-exchanges/src/test/exchange-data/gdax/new_sell_order.json b/bxbot-exchanges/src/test/exchange-data/coinbasepro/new_sell_order.json similarity index 100% rename from bxbot-exchanges/src/test/exchange-data/gdax/new_sell_order.json rename to bxbot-exchanges/src/test/exchange-data/coinbasepro/new_sell_order.json diff --git a/bxbot-exchanges/src/test/exchange-data/gdax/orders.json b/bxbot-exchanges/src/test/exchange-data/coinbasepro/orders.json similarity index 100% rename from bxbot-exchanges/src/test/exchange-data/gdax/orders.json rename to bxbot-exchanges/src/test/exchange-data/coinbasepro/orders.json diff --git a/bxbot-exchanges/src/test/exchange-data/gdax/stats.json b/bxbot-exchanges/src/test/exchange-data/coinbasepro/stats.json similarity index 100% rename from bxbot-exchanges/src/test/exchange-data/gdax/stats.json rename to bxbot-exchanges/src/test/exchange-data/coinbasepro/stats.json diff --git a/bxbot-exchanges/src/test/exchange-data/gdax/ticker.json b/bxbot-exchanges/src/test/exchange-data/coinbasepro/ticker.json similarity index 100% rename from bxbot-exchanges/src/test/exchange-data/gdax/ticker.json rename to bxbot-exchanges/src/test/exchange-data/coinbasepro/ticker.json diff --git a/bxbot-exchanges/src/test/exchange-data/okcoin/cancel_order-error.json b/bxbot-exchanges/src/test/exchange-data/okcoin/cancel_order-error.json deleted file mode 100644 index a081b1ff9..000000000 --- a/bxbot-exchanges/src/test/exchange-data/okcoin/cancel_order-error.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "error_code": 10009, - "result": false -} \ No newline at end of file diff --git a/bxbot-exchanges/src/test/exchange-data/okcoin/cancel_order.json b/bxbot-exchanges/src/test/exchange-data/okcoin/cancel_order.json deleted file mode 100644 index 4b31b3f50..000000000 --- a/bxbot-exchanges/src/test/exchange-data/okcoin/cancel_order.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "order_id": 99663577, - "result": true -} \ No newline at end of file diff --git a/bxbot-exchanges/src/test/exchange-data/okcoin/depth.json b/bxbot-exchanges/src/test/exchange-data/okcoin/depth.json deleted file mode 100644 index 9754d5a15..000000000 --- a/bxbot-exchanges/src/test/exchange-data/okcoin/depth.json +++ /dev/null @@ -1,1606 +0,0 @@ -{ - "asks": [ - [ - 261.86, - 85 - ], - [ - 261.79, - 4.645 - ], - [ - 261.56, - 15.117 - ], - [ - 261.51, - 5.622 - ], - [ - 261.16, - 1.096 - ], - [ - 260.86, - 3.393 - ], - [ - 260.64, - 2.02 - ], - [ - 260.41, - 4.62 - ], - [ - 260.16, - 3.398 - ], - [ - 260, - 0.694 - ], - [ - 259.99, - 0.4 - ], - [ - 259.88, - 8.814 - ], - [ - 259.58, - 84.5 - ], - [ - 259.43, - 12.446 - ], - [ - 259.2, - 44.452 - ], - [ - 259, - 1 - ], - [ - 258.93, - 0.605 - ], - [ - 258.7, - 18.771 - ], - [ - 258.39, - 0.614 - ], - [ - 258.11, - 3.312 - ], - [ - 257.85, - 35.784 - ], - [ - 257.64, - 46.152 - ], - [ - 257.36, - 1.564 - ], - [ - 257.3, - 84 - ], - [ - 257.04, - 0.538 - ], - [ - 257, - 0.099 - ], - [ - 256.63, - 11.454 - ], - [ - 256.35, - 3.92 - ], - [ - 256.1, - 27.932 - ], - [ - 256, - 2 - ], - [ - 255.84, - 10.555 - ], - [ - 255.39, - 8.298 - ], - [ - 255.13, - 3.544 - ], - [ - 255.02, - 83.5 - ], - [ - 255, - 5 - ], - [ - 254.71, - 3.3 - ], - [ - 254.23, - 4.748 - ], - [ - 253.96, - 9.516 - ], - [ - 253.6, - 42.027 - ], - [ - 253.26, - 6.984 - ], - [ - 253.03, - 8.835 - ], - [ - 252.75, - 83 - ], - [ - 252.73, - 2.683 - ], - [ - 252.48, - 4.401 - ], - [ - 252.26, - 47.843 - ], - [ - 252.04, - 39.478 - ], - [ - 251.66, - 2.952 - ], - [ - 251.43, - 2.036 - ], - [ - 251.01, - 4.08 - ], - [ - 250.74, - 1.534 - ], - [ - 250.47, - 82.5 - ], - [ - 250.36, - 6.971 - ], - [ - 250.1, - 9.192 - ], - [ - 250, - 0.2 - ], - [ - 249.99, - 10.9 - ], - [ - 249.95, - 50 - ], - [ - 249.63, - 5.364 - ], - [ - 249.53, - 0.999 - ], - [ - 249.35, - 3.865 - ], - [ - 249.11, - 5.184 - ], - [ - 248.99, - 0.314 - ], - [ - 248.74, - 0.55 - ], - [ - 248.33, - 2.782 - ], - [ - 248.19, - 82 - ], - [ - 248.12, - 4.436 - ], - [ - 247.75, - 3.95 - ], - [ - 247.45, - 4.609 - ], - [ - 247.03, - 1.552 - ], - [ - 246.57, - 2.928 - ], - [ - 246.21, - 8.232 - ], - [ - 246, - 5.163 - ], - [ - 245.92, - 81.5 - ], - [ - 245.87, - 1.928 - ], - [ - 245.63, - 0.392 - ], - [ - 245.44, - 3.265 - ], - [ - 245.2, - 0.573 - ], - [ - 245, - 10 - ], - [ - 244.72, - 24.816 - ], - [ - 244.46, - 7.512 - ], - [ - 244.24, - 0.705 - ], - [ - 244, - 0.416 - ], - [ - 243.85, - 3.123 - ], - [ - 243.64, - 81 - ], - [ - 243.54, - 2.616 - ], - [ - 243.24, - 5.255 - ], - [ - 242.83, - 45.24 - ], - [ - 242.5, - 0.5 - ], - [ - 242.39, - 33.144 - ], - [ - 242.18, - 6.419 - ], - [ - 242.02, - 0.2 - ], - [ - 241.89, - 3.768 - ], - [ - 241.58, - 1.833 - ], - [ - 241.36, - 80.5 - ], - [ - 241.3, - 3.902 - ], - [ - 241.09, - 9.336 - ], - [ - 241, - 20.774 - ], - [ - 240.84, - 0.786 - ], - [ - 240.58, - 35.471 - ], - [ - 240.5, - 0.038 - ], - [ - 240.29, - 2.704 - ], - [ - 240, - 1.6 - ], - [ - 239.99, - 0.4 - ], - [ - 239.84, - 4.837 - ], - [ - 239.57, - 19.728 - ], - [ - 239.53, - 0.999 - ], - [ - 239.22, - 1.042 - ], - [ - 239.09, - 80 - ], - [ - 239, - 0.51 - ], - [ - 238.87, - 0.144 - ], - [ - 238.64, - 3.764 - ], - [ - 238.26, - 4.766 - ], - [ - 238.05, - 32.567 - ], - [ - 237.76, - 3.552 - ], - [ - 237.31, - 2.971 - ], - [ - 237.03, - 2.985 - ], - [ - 236.77, - 3.575 - ], - [ - 236.39, - 9.576 - ], - [ - 235.99, - 4.176 - ], - [ - 235.76, - 1.728 - ], - [ - 235.52, - 4.896 - ], - [ - 235.31, - 5 - ], - [ - 235.09, - 13.469 - ], - [ - 235.08, - 7.247 - ], - [ - 235, - 35.5 - ], - [ - 234.82, - 4.944 - ], - [ - 234.41, - 4.758 - ], - [ - 234.3, - 0.103 - ], - [ - 234.12, - 4.368 - ], - [ - 234, - 0.681 - ], - [ - 233.99, - 0.089 - ], - [ - 233.84, - 8.771 - ], - [ - 233.78, - 0.332 - ], - [ - 233.63, - 7.38 - ], - [ - 233.33, - 7.343 - ], - [ - 232.97, - 0.199 - ], - [ - 232.64, - 26.652 - ], - [ - 232.51, - 0.1 - ], - [ - 232.49, - 0.088 - ], - [ - 232.34, - 19.019 - ], - [ - 232.2, - 0.39 - ], - [ - 232, - 0.5 - ], - [ - 231.86, - 6.432 - ], - [ - 231.66, - 1.376 - ], - [ - 231.64, - 0.02 - ], - [ - 231.46, - 3.513 - ], - [ - 231.09, - 1.491 - ], - [ - 231, - 4.593 - ], - [ - 230.89, - 4.875 - ], - [ - 230.86, - 0.61 - ], - [ - 230.63, - 4.6 - ], - [ - 230.55, - 0.02 - ], - [ - 230.19, - 0.28 - ], - [ - 230.17, - 6.923 - ], - [ - 230, - 10 - ], - [ - 229.93, - 2.724 - ], - [ - 229.8, - 7.042 - ], - [ - 229.6, - 0.01 - ], - [ - 229.58, - 3.107 - ], - [ - 229.51, - 0.1 - ], - [ - 229.5, - 3.19 - ], - [ - 229.46, - 0.02 - ], - [ - 229.4, - 0.01 - ], - [ - 229.38, - 3.741 - ], - [ - 229.3, - 0.01 - ], - [ - 229.2, - 0.01 - ], - [ - 229.13, - 12 - ], - [ - 229.1, - 0.01 - ], - [ - 229.08, - 2.841 - ], - [ - 229.04, - 0.14 - ], - [ - 229.01, - 0.212 - ], - [ - 229, - 0.01 - ], - [ - 228.96, - 0.01 - ], - [ - 228.92, - 0.01 - ], - [ - 228.9, - 25.01 - ], - [ - 228.88, - 0.01 - ], - [ - 228.85, - 4.272 - ], - [ - 228.84, - 0.01 - ], - [ - 228.8, - 0.02 - ], - [ - 228.76, - 0.01 - ], - [ - 228.72, - 0.01 - ], - [ - 228.7, - 0.01 - ], - [ - 228.69, - 0.802 - ], - [ - 228.68, - 32.391 - ], - [ - 228.67, - 14.77 - ], - [ - 228.66, - 4.774 - ], - [ - 228.64, - 0.01 - ], - [ - 228.63, - 0.028 - ], - [ - 228.6, - 0.039 - ], - [ - 228.56, - 0.01 - ], - [ - 228.52, - 0.311 - ], - [ - 228.49, - 0.346 - ], - [ - 228.48, - 0.01 - ], - [ - 228.46, - 0.345 - ], - [ - 228.45, - 2.176 - ], - [ - 228.44, - 0.01 - ], - [ - 228.43, - 1.306 - ], - [ - 228.42, - 0.532 - ], - [ - 228.4, - 0.26 - ], - [ - 228.38, - 0.1 - ], - [ - 228.36, - 0.01 - ] - ], - "bids": [ - [ - 228.3, - 52.995 - ], - [ - 228.29, - 0.18 - ], - [ - 228.28, - 0.11 - ], - [ - 228.26, - 0.172 - ], - [ - 228.24, - 0.01 - ], - [ - 228.23, - 0.287 - ], - [ - 228.2, - 4.174 - ], - [ - 228.16, - 0.01 - ], - [ - 228.15, - 0.202 - ], - [ - 228.12, - 0.207 - ], - [ - 228.09, - 0.202 - ], - [ - 228.08, - 0.01 - ], - [ - 228.04, - 0.01 - ], - [ - 228, - 11.01 - ], - [ - 227.99, - 9.155 - ], - [ - 227.96, - 0.01 - ], - [ - 227.92, - 0.01 - ], - [ - 227.88, - 0.01 - ], - [ - 227.84, - 0.01 - ], - [ - 227.8, - 0.01 - ], - [ - 227.76, - 0.01 - ], - [ - 227.72, - 4.846 - ], - [ - 227.6, - 0.01 - ], - [ - 227.52, - 2.483 - ], - [ - 227.5, - 0.01 - ], - [ - 227.4, - 0.01 - ], - [ - 227.32, - 1.88 - ], - [ - 227.31, - 4.116 - ], - [ - 227.3, - 0.01 - ], - [ - 227.2, - 0.01 - ], - [ - 227.1, - 0.01 - ], - [ - 227.01, - 1.1 - ], - [ - 227, - 0.01 - ], - [ - 226.97, - 0.1 - ], - [ - 226.95, - 2.713 - ], - [ - 226.9, - 0.01 - ], - [ - 226.8, - 0.01 - ], - [ - 226.7, - 0.15 - ], - [ - 226.59, - 1.916 - ], - [ - 226.51, - 1.48 - ], - [ - 226.5, - 4 - ], - [ - 226.32, - 1.938 - ], - [ - 226.03, - 2.2 - ], - [ - 226.02, - 4.827 - ], - [ - 226.01, - 7.85 - ], - [ - 226, - 17 - ], - [ - 225.66, - 0.805 - ], - [ - 225.65, - 0.877 - ], - [ - 225.51, - 2.84 - ], - [ - 225.5, - 6 - ], - [ - 225.37, - 0.29 - ], - [ - 225.21, - 4.21 - ], - [ - 225.2, - 9.24 - ], - [ - 225.01, - 0.1 - ], - [ - 225, - 13.207 - ], - [ - 224.83, - 0.398 - ], - [ - 224.82, - 3.664 - ], - [ - 224.78, - 0.02 - ], - [ - 224.62, - 13.2 - ], - [ - 224.51, - 0.1 - ], - [ - 224.34, - 3 - ], - [ - 224.06, - 7.56 - ], - [ - 224.01, - 0.1 - ], - [ - 223.77, - 3.522 - ], - [ - 223.71, - 0.02 - ], - [ - 223.5, - 5.388 - ], - [ - 223.3, - 18 - ], - [ - 223.25, - 3.004 - ], - [ - 223.05, - 0.969 - ], - [ - 222.77, - 2.627 - ], - [ - 222.67, - 3.061 - ], - [ - 222.64, - 0.02 - ], - [ - 222.55, - 3.298 - ], - [ - 222.28, - 15.888 - ], - [ - 222, - 0.325 - ], - [ - 221.94, - 1.33 - ], - [ - 221.71, - 4.898 - ], - [ - 221.66, - 1.236 - ], - [ - 221.5, - 0.5 - ], - [ - 221.4, - 9.251 - ], - [ - 221.14, - 0.031 - ], - [ - 220.93, - 3.083 - ], - [ - 220.6, - 0.425 - ], - [ - 220.54, - 2.082 - ], - [ - 220.32, - 2.784 - ], - [ - 220.21, - 0.48 - ], - [ - 220.2, - 0.545 - ], - [ - 220.03, - 4.008 - ], - [ - 220, - 11.32 - ], - [ - 219.77, - 3.683 - ], - [ - 219.55, - 6.276 - ], - [ - 219.33, - 3.528 - ], - [ - 219.05, - 15.312 - ], - [ - 218.81, - 3.805 - ], - [ - 218.53, - 9.36 - ], - [ - 218.5, - 0.5 - ], - [ - 218.22, - 0.212 - ], - [ - 218.08, - 0.344 - ], - [ - 217.88, - 29.736 - ], - [ - 217.44, - 7.98 - ], - [ - 217.08, - 0.326 - ], - [ - 216.68, - 2.372 - ], - [ - 216.32, - 80 - ], - [ - 216.3, - 5.25 - ], - [ - 216.19, - 1.056 - ], - [ - 216, - 0.5 - ], - [ - 215.91, - 15 - ], - [ - 215.55, - 24.912 - ], - [ - 215.25, - 5.076 - ], - [ - 215, - 2 - ], - [ - 214.92, - 3.288 - ], - [ - 214.69, - 3.203 - ], - [ - 214.26, - 3.785 - ], - [ - 214.06, - 2.455 - ], - [ - 214.04, - 80.5 - ], - [ - 213.78, - 3.84 - ], - [ - 213.46, - 4.329 - ], - [ - 213.26, - 4.823 - ], - [ - 213.02, - 6.684 - ], - [ - 213, - 0.35 - ], - [ - 212.81, - 0.688 - ], - [ - 212.55, - 0.746 - ], - [ - 212.21, - 3.006 - ], - [ - 212, - 0.095 - ], - [ - 211.9, - 8.664 - ], - [ - 211.76, - 92.521 - ], - [ - 211.59, - 4.762 - ], - [ - 211.35, - 6.768 - ], - [ - 211.14, - 3.775 - ], - [ - 210.81, - 9.6 - ], - [ - 210.58, - 1.557 - ], - [ - 210.27, - 4.032 - ], - [ - 210.21, - 0.51 - ], - [ - 210, - 47.27 - ], - [ - 209.98, - 8.616 - ], - [ - 209.76, - 32.604 - ], - [ - 209.48, - 98.318 - ], - [ - 209.13, - 3.271 - ], - [ - 208.77, - 3.116 - ], - [ - 208.44, - 14.905 - ], - [ - 208.03, - 5.604 - ], - [ - 208, - 44.348 - ], - [ - 207.77, - 40.68 - ], - [ - 207.33, - 2.283 - ], - [ - 207.21, - 82 - ], - [ - 207.13, - 0.888 - ], - [ - 206.68, - 6.552 - ], - [ - 206.22, - 5.782 - ], - [ - 205.93, - 6.594 - ], - [ - 205.45, - 3.816 - ], - [ - 205.09, - 0.876 - ], - [ - 204.93, - 82.5 - ], - [ - 204.89, - 1.12 - ], - [ - 204.51, - 3.096 - ], - [ - 204.44, - 2.5 - ], - [ - 204.27, - 2.985 - ], - [ - 203.97, - 0.897 - ], - [ - 203.61, - 3.426 - ], - [ - 203.3, - 7.608 - ], - [ - 203.03, - 4.752 - ], - [ - 202.75, - 2.675 - ], - [ - 202.65, - 83 - ], - [ - 202.57, - 0.14 - ], - [ - 202.44, - 2.5 - ], - [ - 202.43, - 1.836 - ], - [ - 202.22, - 7.468 - ], - [ - 202.11, - 0.44 - ], - [ - 202.02, - 2 - ], - [ - 202, - 2 - ], - [ - 201.82, - 3.148 - ], - [ - 201.38, - 3.852 - ], - [ - 201.02, - 0.471 - ], - [ - 200.84, - 0.615 - ], - [ - 200.8, - 10 - ], - [ - 200.73, - 6.624 - ], - [ - 200.7, - 5.12 - ], - [ - 200.6, - 3.56 - ], - [ - 200.48, - 3.255 - ], - [ - 200.38, - 83.5 - ], - [ - 200, - 6.44 - ], - [ - 199.76, - 8.616 - ], - [ - 199.46, - 4.324 - ], - [ - 199.17, - 1.378 - ], - [ - 198.97, - 3.999 - ], - [ - 198.75, - 2.217 - ], - [ - 198.34, - 22.589 - ], - [ - 198.33, - 47.15 - ], - [ - 198.1, - 84 - ], - [ - 197.89, - 0.901 - ], - [ - 197.62, - 1.124 - ], - [ - 197.34, - 4.022 - ], - [ - 197.01, - 8.652 - ], - [ - 196.79, - 14.627 - ], - [ - 196.47, - 14.664 - ], - [ - 196.22, - 2.75 - ], - [ - 196.01, - 7.983 - ], - [ - 195.82, - 84.5 - ], - [ - 195.63, - 18.215 - ], - [ - 195.34, - 3.695 - ], - [ - 195.26, - 0.486 - ] - ] -} \ No newline at end of file diff --git a/bxbot-exchanges/src/test/exchange-data/okcoin/order_info-error.json b/bxbot-exchanges/src/test/exchange-data/okcoin/order_info-error.json deleted file mode 100644 index abb84434d..000000000 --- a/bxbot-exchanges/src/test/exchange-data/okcoin/order_info-error.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "error_code": 10012, - "result": false -} \ No newline at end of file diff --git a/bxbot-exchanges/src/test/exchange-data/okcoin/order_info.json b/bxbot-exchanges/src/test/exchange-data/okcoin/order_info.json deleted file mode 100644 index cac7e8726..000000000 --- a/bxbot-exchanges/src/test/exchange-data/okcoin/order_info.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "orders": [ - { - "amount": 0.015, - "avg_price": 0, - "create_date": 1442949893000, - "deal_amount": 0, - "order_id": 99031951, - "orders_id": 99031951, - "price": 255, - "status": 0, - "symbol": "btc_usd", - "type": "sell" - }, - { - "amount": 0.01, - "avg_price": 0, - "create_date": 1442949883000, - "deal_amount": 0, - "order_id": 99031882, - "orders_id": 99031882, - "price": 250, - "status": 0, - "symbol": "btc_usd", - "type": "sell" - } - ], - "result": true -} \ No newline at end of file diff --git a/bxbot-exchanges/src/test/exchange-data/okcoin/ticker.json b/bxbot-exchanges/src/test/exchange-data/okcoin/ticker.json deleted file mode 100644 index 49df46c51..000000000 --- a/bxbot-exchanges/src/test/exchange-data/okcoin/ticker.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "date": "1442673698", - "ticker": { - "buy": "231.32", - "high": "233.6", - "last": "231.35", - "low": "231.01", - "sell": "231.4", - "vol": "5465.046" - } -} \ No newline at end of file diff --git a/bxbot-exchanges/src/test/exchange-data/okcoin/trade-error.json b/bxbot-exchanges/src/test/exchange-data/okcoin/trade-error.json deleted file mode 100644 index 8964ecd7c..000000000 --- a/bxbot-exchanges/src/test/exchange-data/okcoin/trade-error.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "error_code": 10008, - "result": false -} \ No newline at end of file diff --git a/bxbot-exchanges/src/test/exchange-data/okcoin/trade_buy.json b/bxbot-exchanges/src/test/exchange-data/okcoin/trade_buy.json deleted file mode 100644 index 20b5e2c70..000000000 --- a/bxbot-exchanges/src/test/exchange-data/okcoin/trade_buy.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "order_id": 99646259, - "result": true -} \ No newline at end of file diff --git a/bxbot-exchanges/src/test/exchange-data/okcoin/trade_sell.json b/bxbot-exchanges/src/test/exchange-data/okcoin/trade_sell.json deleted file mode 100644 index 4c1e6e4a4..000000000 --- a/bxbot-exchanges/src/test/exchange-data/okcoin/trade_sell.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "order_id": 99646257, - "result": true -} \ No newline at end of file diff --git a/bxbot-exchanges/src/test/exchange-data/okcoin/userinfo-error.json b/bxbot-exchanges/src/test/exchange-data/okcoin/userinfo-error.json deleted file mode 100644 index 8514627f1..000000000 --- a/bxbot-exchanges/src/test/exchange-data/okcoin/userinfo-error.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "error_code": 10005, - "result": false -} \ No newline at end of file diff --git a/bxbot-exchanges/src/test/exchange-data/okcoin/userinfo.json b/bxbot-exchanges/src/test/exchange-data/okcoin/userinfo.json deleted file mode 100644 index 87268d826..000000000 --- a/bxbot-exchanges/src/test/exchange-data/okcoin/userinfo.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "info": { - "funds": { - "asset": { - "net": "23.12", - "total": "23.12" - }, - "free": { - "btc": "0.06", - "ltc": "0", - "usd": "0.0608" - }, - "freezed": { - "btc": "0.03", - "ltc": "0", - "usd": "2.25" - } - } - }, - "result": true -} \ No newline at end of file diff --git a/bxbot-exchanges/src/test/java/com/gazbert/bxbot/exchanges/TestGdaxExchangeAdapter.java b/bxbot-exchanges/src/test/java/com/gazbert/bxbot/exchanges/TestCoinbaseProExchangeAdapter.java similarity index 89% rename from bxbot-exchanges/src/test/java/com/gazbert/bxbot/exchanges/TestGdaxExchangeAdapter.java rename to bxbot-exchanges/src/test/java/com/gazbert/bxbot/exchanges/TestCoinbaseProExchangeAdapter.java index 3528e0b4d..6d30e1c44 100644 --- a/bxbot-exchanges/src/test/java/com/gazbert/bxbot/exchanges/TestGdaxExchangeAdapter.java +++ b/bxbot-exchanges/src/test/java/com/gazbert/bxbot/exchanges/TestCoinbaseProExchangeAdapter.java @@ -2,6 +2,7 @@ * The MIT License (MIT) * * Copyright (c) 2015 Gareth Jon Lynch + * Copyright (c) 2019 David Huertas * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -68,12 +69,9 @@ import org.powermock.modules.junit4.PowerMockRunner; /** - * Tests the behaviour of the GDAX Exchange Adapter. + * Tests the behaviour of the COINBASE PRO Exchange Adapter. * - *

DO NOT USE: See https://github.com/gazbert/bxbot/pull/120 - * - * @author gazbert - * @deprecated #120 : GDAX exchange has been superseded by Coinbase Pro: https://pro.coinbase.com/ + * @author davidhuertas */ @RunWith(PowerMockRunner.class) @PowerMockIgnore({ @@ -85,22 +83,24 @@ "org.w3c.dom.*", "javax.xml.datatype.*" }) -@PrepareForTest(GdaxExchangeAdapter.class) -@Deprecated(forRemoval = true) -public class TestGdaxExchangeAdapter extends AbstractExchangeAdapterTest { +@PrepareForTest(CoinbaseProExchangeAdapter.class) +public class TestCoinbaseProExchangeAdapter extends AbstractExchangeAdapterTest { - private static final String BOOK_JSON_RESPONSE = "./src/test/exchange-data/gdax/book.json"; - private static final String ORDERS_JSON_RESPONSE = "./src/test/exchange-data/gdax/orders.json"; + private static final String BOOK_JSON_RESPONSE = "./src/test/exchange-data/coinbasepro/book.json"; + private static final String ORDERS_JSON_RESPONSE = + "./src/test/exchange-data/coinbasepro/orders.json"; private static final String ACCOUNTS_JSON_RESPONSE = - "./src/test/exchange-data/gdax/accounts.json"; - private static final String TICKER_JSON_RESPONSE = "./src/test/exchange-data/gdax/ticker.json"; + "./src/test/exchange-data/coinbasepro/accounts.json"; + private static final String TICKER_JSON_RESPONSE = + "./src/test/exchange-data/coinbasepro/ticker.json"; private static final String NEW_BUY_ORDER_JSON_RESPONSE = - "./src/test/exchange-data/gdax/new_buy_order.json"; + "./src/test/exchange-data/coinbasepro/new_buy_order.json"; private static final String NEW_SELL_ORDER_JSON_RESPONSE = - "./src/test/exchange-data/gdax/new_sell_order.json"; + "./src/test/exchange-data/coinbasepro/new_sell_order.json"; private static final String CANCEL_ORDER_JSON_RESPONSE = - "./src/test/exchange-data/gdax/cancel.json"; - private static final String STATS_JSON_RESPONSE = "./src/test/exchange-data/gdax/stats.json"; + "./src/test/exchange-data/coinbasepro/cancel.json"; + private static final String STATS_JSON_RESPONSE = + "./src/test/exchange-data/coinbasepro/stats.json"; private static final String MARKET_ID = "BTC-GBP"; private static final String ORDER_BOOK_DEPTH_LEVEL = @@ -137,7 +137,7 @@ public class TestGdaxExchangeAdapter extends AbstractExchangeAdapterTest { "Connection reset", "Remote host closed connection during handshake"); - private static final String PUBLIC_API_BASE_URL = "https://api.gdax.com/"; + private static final String PUBLIC_API_BASE_URL = "https://api.pro.coinbase.com/"; private static final String AUTHENTICATED_API_URL = PUBLIC_API_BASE_URL; private ExchangeConfig exchangeConfig; @@ -145,9 +145,7 @@ public class TestGdaxExchangeAdapter extends AbstractExchangeAdapterTest { private NetworkConfig networkConfig; private OtherConfig otherConfig; - /** - * Create some exchange config - the TradingEngine would normally do this. - */ + /** Create some exchange config - the TradingEngine would normally do this. */ @Before public void setupForEachTest() { authenticationConfig = PowerMock.createMock(AuthenticationConfig.class); @@ -163,6 +161,7 @@ public void setupForEachTest() { otherConfig = PowerMock.createMock(OtherConfig.class); expect(otherConfig.getItem("buy-fee")).andReturn("0.25"); expect(otherConfig.getItem("sell-fee")).andReturn("0.25"); + expect(otherConfig.getItem("time-server-bias")).andReturn("82"); exchangeConfig = PowerMock.createMock(ExchangeConfig.class); expect(exchangeConfig.getAuthenticationConfig()).andReturn(authenticationConfig); @@ -201,9 +200,9 @@ public void testCreateOrderToBuyIsSuccessful() throws Exception { expect(requestParamMap.put("product_id", MARKET_ID)).andStubReturn(null); // Partial mock so we do not send stuff down the wire - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, + CoinbaseProExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD); @@ -250,9 +249,9 @@ public void testCreateOrderToSellIsSuccessful() throws Exception { expect(requestParamMap.put("side", "sell")).andStubReturn(null); expect(requestParamMap.put("product_id", MARKET_ID)).andStubReturn(null); - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, + CoinbaseProExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD); @@ -279,9 +278,9 @@ public void testCreateOrderToSellIsSuccessful() throws Exception { @Test(expected = ExchangeNetworkException.class) public void testCreateOrderHandlesExchangeNetworkException() throws Exception { - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, @@ -302,9 +301,9 @@ public void testCreateOrderHandlesExchangeNetworkException() throws Exception { @Test(expected = TradingApiException.class) public void testCreateOrderHandlesUnexpectedException() throws Exception { - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, @@ -334,9 +333,9 @@ public void testCancelOrderIsSuccessful() throws Exception { new AbstractExchangeAdapter.ExchangeHttpResponse( 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, @@ -357,9 +356,9 @@ public void testCancelOrderIsSuccessful() throws Exception { @Test(expected = ExchangeNetworkException.class) public void testCancelOrderHandlesExchangeNetworkException() throws Exception { - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, @@ -382,9 +381,9 @@ public void testCancelOrderHandlesExchangeNetworkException() throws Exception { @Test(expected = TradingApiException.class) public void testCancelOrderHandlesUnexpectedException() throws Exception { - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, @@ -415,9 +414,9 @@ public void testGettingYourOpenOrdersSuccessfully() throws Exception { new AbstractExchangeAdapter.ExchangeHttpResponse( 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, @@ -456,9 +455,9 @@ public void testGettingYourOpenOrdersSuccessfully() throws Exception { @Test(expected = ExchangeNetworkException.class) public void testGettingYourOpenOrdersHandlesExchangeNetworkException() throws Exception { - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, @@ -476,9 +475,9 @@ public void testGettingYourOpenOrdersHandlesExchangeNetworkException() throws Ex @Test(expected = TradingApiException.class) public void testGettingYourOpenOrdersHandlesUnexpectedException() throws Exception { - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, @@ -511,9 +510,9 @@ public void testGettingMarketOrders() throws Exception { final Map requestParamMap = PowerMock.createMock(Map.class); expect(requestParamMap.put("level", ORDER_BOOK_DEPTH_LEVEL)).andStubReturn(null); - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, + CoinbaseProExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD); @@ -559,9 +558,9 @@ public void testGettingMarketOrders() throws Exception { @Test(expected = ExchangeNetworkException.class) public void testGettingMarketOrdersHandlesExchangeNetworkException() throws Exception { - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, @@ -579,9 +578,9 @@ public void testGettingMarketOrdersHandlesExchangeNetworkException() throws Exce @Test(expected = TradingApiException.class) public void testGettingMarketOrdersHandlesUnexpectedException() throws Exception { - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, @@ -613,9 +612,9 @@ public void testGettingLatestMarketPriceSuccessfully() throws Exception { new AbstractExchangeAdapter.ExchangeHttpResponse( 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, eq(TICKER), eq(null)) @@ -633,9 +632,9 @@ exchangeAdapter, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, eq(TICKER), eq(n @Test(expected = ExchangeNetworkException.class) public void testGettingLatestMarketPriceHandlesExchangeNetworkException() throws Exception { - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, eq(TICKER), eq(null)) .andThrow( @@ -650,9 +649,9 @@ exchangeAdapter, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, eq(TICKER), eq(n @Test(expected = TradingApiException.class) public void testGettingLatestMarketPriceHandlesUnexpectedException() throws Exception { - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, eq(TICKER), eq(null)) .andThrow(new IllegalArgumentException("Come with me if you want to live.")); @@ -675,9 +674,9 @@ public void testGettingBalanceInfoSuccessfully() throws Exception { new AbstractExchangeAdapter.ExchangeHttpResponse( 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, @@ -725,9 +724,9 @@ public void testGettingBalanceInfoSuccessfully() throws Exception { @Test(expected = ExchangeNetworkException.class) public void testGettingBalanceInfoHandlesExchangeNetworkException() throws Exception { - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, @@ -747,9 +746,9 @@ public void testGettingBalanceInfoHandlesExchangeNetworkException() throws Excep @Test(expected = TradingApiException.class) public void testGettingBalanceInfoHandlesUnexpectedException() throws Exception { - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, @@ -783,9 +782,9 @@ public void testGettingTickerSuccessfully() throws Exception { new AbstractExchangeAdapter.ExchangeHttpResponse( 200, "OK", new String(encodedStats, StandardCharsets.UTF_8)); - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, eq(TICKER), eq(null)) @@ -806,7 +805,7 @@ exchangeAdapter, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, eq(STATS), eq(nu assertEquals(0, ticker.getLow().compareTo(new BigDecimal("13409.97000000"))); assertEquals(0, ticker.getOpen().compareTo(new BigDecimal("13609.53000000"))); assertEquals(0, ticker.getVolume().compareTo(new BigDecimal("607.54445656"))); - assertNull(ticker.getVwap()); // not provided by GDAX + assertNull(ticker.getVwap()); // not provided by COINBASE PRO assertEquals(1508008776604L, (long) ticker.getTimestamp()); PowerMock.verifyAll(); @@ -814,9 +813,9 @@ exchangeAdapter, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, eq(STATS), eq(nu @Test(expected = ExchangeNetworkException.class) public void testGettingTickerHandlesExchangeNetworkException() throws Exception { - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, eq(TICKER), eq(null)) .andThrow( @@ -833,9 +832,9 @@ exchangeAdapter, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, eq(TICKER), eq(n @Test(expected = TradingApiException.class) public void testGettingTickerHandlesUnexpectedException() throws Exception { - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); PowerMock.expectPrivate( exchangeAdapter, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, eq(TICKER), eq(null)) .andThrow( @@ -858,7 +857,7 @@ exchangeAdapter, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, eq(TICKER), eq(n @Test public void testGettingExchangeSellingFeeIsAsExpected() { PowerMock.replayAll(); - final GdaxExchangeAdapter exchangeAdapter = new GdaxExchangeAdapter(); + final CoinbaseProExchangeAdapter exchangeAdapter = new CoinbaseProExchangeAdapter(); exchangeAdapter.init(exchangeConfig); final BigDecimal sellPercentageFee = @@ -870,7 +869,7 @@ public void testGettingExchangeSellingFeeIsAsExpected() { @Test public void testGettingExchangeBuyingFeeIsAsExpected() { PowerMock.replayAll(); - final GdaxExchangeAdapter exchangeAdapter = new GdaxExchangeAdapter(); + final CoinbaseProExchangeAdapter exchangeAdapter = new CoinbaseProExchangeAdapter(); exchangeAdapter.init(exchangeConfig); final BigDecimal buyPercentageFee = @@ -882,10 +881,10 @@ public void testGettingExchangeBuyingFeeIsAsExpected() { @Test public void testGettingImplNameIsAsExpected() { PowerMock.replayAll(); - final GdaxExchangeAdapter exchangeAdapter = new GdaxExchangeAdapter(); + final CoinbaseProExchangeAdapter exchangeAdapter = new CoinbaseProExchangeAdapter(); exchangeAdapter.init(exchangeConfig); - assertEquals("GDAX REST API v1", exchangeAdapter.getImplName()); + assertEquals("COINBASE PRO REST API v1", exchangeAdapter.getImplName()); PowerMock.verifyAll(); } @@ -897,7 +896,7 @@ public void testGettingImplNameIsAsExpected() { public void testExchangeAdapterInitialisesSuccessfully() { PowerMock.replayAll(); - final GdaxExchangeAdapter exchangeAdapter = new GdaxExchangeAdapter(); + final CoinbaseProExchangeAdapter exchangeAdapter = new CoinbaseProExchangeAdapter(); exchangeAdapter.init(exchangeConfig); assertNotNull(exchangeAdapter); @@ -912,7 +911,7 @@ public void testExchangeAdapterThrowsExceptionIfPassphraseConfigIsMissing() { expect(authenticationConfig.getItem("secret")).andReturn("your_client_secret"); PowerMock.replayAll(); - final ExchangeAdapter exchangeAdapter = new GdaxExchangeAdapter(); + final ExchangeAdapter exchangeAdapter = new CoinbaseProExchangeAdapter(); exchangeAdapter.init(exchangeConfig); PowerMock.verifyAll(); @@ -926,7 +925,7 @@ public void testExchangeAdapterThrowsExceptionIfPublicKeyConfigIsMissing() { expect(authenticationConfig.getItem("secret")).andReturn("your_client_secret"); PowerMock.replayAll(); - final ExchangeAdapter exchangeAdapter = new GdaxExchangeAdapter(); + final ExchangeAdapter exchangeAdapter = new CoinbaseProExchangeAdapter(); exchangeAdapter.init(exchangeConfig); PowerMock.verifyAll(); @@ -940,7 +939,7 @@ public void testExchangeAdapterThrowsExceptionIfSecretConfigIsMissing() { expect(authenticationConfig.getItem("secret")).andReturn(null); PowerMock.replayAll(); - final ExchangeAdapter exchangeAdapter = new GdaxExchangeAdapter(); + final ExchangeAdapter exchangeAdapter = new CoinbaseProExchangeAdapter(); exchangeAdapter.init(exchangeConfig); PowerMock.verifyAll(); @@ -953,7 +952,7 @@ public void testExchangeAdapterThrowsExceptionIfBuyFeeIsMissing() { expect(otherConfig.getItem("sell-fee")).andReturn("0.25"); PowerMock.replayAll(); - final ExchangeAdapter exchangeAdapter = new GdaxExchangeAdapter(); + final ExchangeAdapter exchangeAdapter = new CoinbaseProExchangeAdapter(); exchangeAdapter.init(exchangeConfig); PowerMock.verifyAll(); @@ -966,7 +965,7 @@ public void testExchangeAdapterThrowsExceptionIfSellFeeIsMissing() { expect(otherConfig.getItem("sell-fee")).andReturn(""); PowerMock.replayAll(); - final ExchangeAdapter exchangeAdapter = new GdaxExchangeAdapter(); + final ExchangeAdapter exchangeAdapter = new CoinbaseProExchangeAdapter(); exchangeAdapter.init(exchangeConfig); PowerMock.verifyAll(); @@ -978,7 +977,7 @@ public void testExchangeAdapterThrowsExceptionIfTimeoutConfigIsMissing() { expect(networkConfig.getConnectionTimeout()).andReturn(0); PowerMock.replayAll(); - final ExchangeAdapter exchangeAdapter = new GdaxExchangeAdapter(); + final ExchangeAdapter exchangeAdapter = new CoinbaseProExchangeAdapter(); exchangeAdapter.init(exchangeConfig); PowerMock.verifyAll(); @@ -999,9 +998,9 @@ public void testSendingPublicRequestToExchangeSuccessfully() throws Exception { new AbstractExchangeAdapter.ExchangeHttpResponse( 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_MAKE_NETWORK_REQUEST_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_MAKE_NETWORK_REQUEST_METHOD); final URL url = new URL(PUBLIC_API_BASE_URL + TICKER); PowerMock.expectPrivate( @@ -1024,9 +1023,9 @@ public void testSendingPublicRequestToExchangeSuccessfully() throws Exception { @Test(expected = ExchangeNetworkException.class) public void testSendingPublicRequestToExchangeHandlesExchangeNetworkException() throws Exception { - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_MAKE_NETWORK_REQUEST_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_MAKE_NETWORK_REQUEST_METHOD); final URL url = new URL(PUBLIC_API_BASE_URL + TICKER); PowerMock.expectPrivate( @@ -1049,9 +1048,9 @@ public void testSendingPublicRequestToExchangeHandlesExchangeNetworkException() @Test(expected = TradingApiException.class) public void testSendingPublicRequestToExchangeHandlesTradingApiException() throws Exception { - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, MOCKED_MAKE_NETWORK_REQUEST_METHOD); + CoinbaseProExchangeAdapter.class, MOCKED_MAKE_NETWORK_REQUEST_METHOD); final URL url = new URL(PUBLIC_API_BASE_URL + TICKER); PowerMock.expectPrivate( @@ -1096,9 +1095,9 @@ public void testSendingAuthenticatedRequestToExchangeSuccessfully() throws Excep expect(requestHeaderMap.put(eq("CB-ACCESS-PASSPHRASE"), eq(PASSPHRASE))).andStubReturn(null); PowerMock.replay(requestHeaderMap); // map needs to be in play early - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, + CoinbaseProExchangeAdapter.class, MOCKED_MAKE_NETWORK_REQUEST_METHOD, MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD); PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD) @@ -1146,9 +1145,9 @@ public void testSendingAuthenticatedRequestToExchangeHandlesExchangeNetworkExcep expect(requestHeaderMap.put(eq("CB-ACCESS-PASSPHRASE"), eq(PASSPHRASE))).andStubReturn(null); PowerMock.replay(requestHeaderMap); // map needs to be in play early - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, + CoinbaseProExchangeAdapter.class, MOCKED_MAKE_NETWORK_REQUEST_METHOD, MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD); PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD) @@ -1197,9 +1196,9 @@ public void testSendingAuthenticatedRequestToExchangeHandlesTradingApiException( expect(requestHeaderMap.put(eq("CB-ACCESS-PASSPHRASE"), eq(PASSPHRASE))).andStubReturn(null); PowerMock.replay(requestHeaderMap); // map needs to be in play early - final GdaxExchangeAdapter exchangeAdapter = + final CoinbaseProExchangeAdapter exchangeAdapter = PowerMock.createPartialMockAndInvokeDefaultConstructor( - GdaxExchangeAdapter.class, + CoinbaseProExchangeAdapter.class, MOCKED_MAKE_NETWORK_REQUEST_METHOD, MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD); PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD) diff --git a/bxbot-exchanges/src/test/java/com/gazbert/bxbot/exchanges/TestOkcoinExchangeAdapter.java b/bxbot-exchanges/src/test/java/com/gazbert/bxbot/exchanges/TestOkcoinExchangeAdapter.java deleted file mode 100644 index cf4890009..000000000 --- a/bxbot-exchanges/src/test/java/com/gazbert/bxbot/exchanges/TestOkcoinExchangeAdapter.java +++ /dev/null @@ -1,1397 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Gareth Jon Lynch - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.gazbert.bxbot.exchanges; - -import static org.easymock.EasyMock.anyObject; -import static org.easymock.EasyMock.anyString; -import static org.easymock.EasyMock.eq; -import static org.easymock.EasyMock.expect; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -import com.gazbert.bxbot.exchange.api.AuthenticationConfig; -import com.gazbert.bxbot.exchange.api.ExchangeConfig; -import com.gazbert.bxbot.exchange.api.NetworkConfig; -import com.gazbert.bxbot.exchange.api.OtherConfig; -import com.gazbert.bxbot.trading.api.BalanceInfo; -import com.gazbert.bxbot.trading.api.ExchangeNetworkException; -import com.gazbert.bxbot.trading.api.MarketOrderBook; -import com.gazbert.bxbot.trading.api.OpenOrder; -import com.gazbert.bxbot.trading.api.OrderType; -import com.gazbert.bxbot.trading.api.Ticker; -import com.gazbert.bxbot.trading.api.TradingApiException; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.text.DecimalFormat; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.powermock.api.easymock.PowerMock; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -/** - * Tests the behaviour of the OKCoin Exchange Adapter. - * - *

DO NOT USE: See https://github.com/gazbert/bxbot/issues/122 - * - * @author gazbert - * @deprecated #120 : The OKCoin V1 API is now deprecated and no longer works - adapter needs - * updating to use V3 API. - */ -@RunWith(PowerMockRunner.class) -@PowerMockIgnore({ - "javax.crypto.*", - "javax.management.*", - "com.sun.org.apache.xerces.*", - "javax.xml.parsers.*", - "org.xml.sax.*", - "org.w3c.dom.*" -}) -@PrepareForTest(OkCoinExchangeAdapter.class) -@Deprecated(forRemoval = true) -public class TestOkcoinExchangeAdapter extends AbstractExchangeAdapterTest { - - private static final String DEPTH_JSON_RESPONSE = "./src/test/exchange-data/okcoin/depth.json"; - private static final String USERINFO_JSON_RESPONSE = - "./src/test/exchange-data/okcoin/userinfo.json"; - private static final String USERINFO_ERROR_JSON_RESPONSE = - "./src/test/exchange-data/okcoin/userinfo-error.json"; - private static final String TICKER_JSON_RESPONSE = "./src/test/exchange-data/okcoin/ticker.json"; - private static final String ORDER_INFO_JSON_RESPONSE = - "./src/test/exchange-data/okcoin/order_info.json"; - private static final String ORDER_INFO_ERROR_JSON_RESPONSE = - "./src/test/exchange-data/okcoin/order_info-error.json"; - private static final String TRADE_BUY_JSON_RESPONSE = - "./src/test/exchange-data/okcoin/trade_buy.json"; - private static final String TRADE_SELL_JSON_RESPONSE = - "./src/test/exchange-data/okcoin/trade_sell.json"; - private static final String TRADE_ERROR_JSON_RESPONSE = - "./src/test/exchange-data/okcoin/trade-error.json"; - private static final String CANCEL_ORDER_JSON_RESPONSE = - "./src/test/exchange-data/okcoin/cancel_order.json"; - private static final String CANCEL_ORDER_ERROR_JSON_RESPONSE = - "./src/test/exchange-data/okcoin/cancel_order-error.json"; - - private static final String DEPTH = "depth.do"; - private static final String ORDER_INFO = "order_info.do"; - private static final String USERINFO = "userinfo.do"; - private static final String TICKER = "ticker.do"; - private static final String TRADE = "trade.do"; - private static final String CANCEL_ORDER = "cancel_order.do"; - - private static final String MARKET_ID = "btc_usd"; - private static final BigDecimal BUY_ORDER_PRICE = new BigDecimal("200.18"); - private static final BigDecimal BUY_ORDER_QUANTITY = new BigDecimal("0.01"); - private static final BigDecimal SELL_ORDER_PRICE = new BigDecimal("300.176"); - private static final BigDecimal SELL_ORDER_QUANTITY = new BigDecimal("0.01"); - private static final String ORDER_ID_TO_CANCEL = "99671870"; - - private static final String MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD = "createRequestParamMap"; - private static final String MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD = - "sendAuthenticatedRequestToExchange"; - private static final String MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD = - "sendPublicRequestToExchange"; - private static final String MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD = "createHeaderParamMap"; - private static final String MOCKED_MAKE_NETWORK_REQUEST_METHOD = "makeNetworkRequest"; - - private static final String KEY = "key123"; - private static final String SECRET = "notGonnaTellYa"; - private static final List nonFatalNetworkErrorCodes = Arrays.asList(502, 503, 504); - private static final List nonFatalNetworkErrorMessages = - Arrays.asList( - "Connection refused", - "Connection reset", - "Remote host closed connection during handshake"); - - private static final String OKCOIN_API_VERSION = "v1"; - private static final String PUBLIC_API_BASE_URL = - "https://www.okcoin.com/api/" + OKCOIN_API_VERSION + "/"; - private static final String AUTHENTICATED_API_URL = PUBLIC_API_BASE_URL; - - private ExchangeConfig exchangeConfig; - private AuthenticationConfig authenticationConfig; - private NetworkConfig networkConfig; - private OtherConfig otherConfig; - - /** Create some exchange config - the TradingEngine would normally do this. */ - @Before - public void setupForEachTest() { - authenticationConfig = PowerMock.createMock(AuthenticationConfig.class); - expect(authenticationConfig.getItem("key")).andReturn(KEY); - expect(authenticationConfig.getItem("secret")).andReturn(SECRET); - - networkConfig = PowerMock.createMock(NetworkConfig.class); - expect(networkConfig.getConnectionTimeout()).andReturn(30); - expect(networkConfig.getNonFatalErrorCodes()).andReturn(nonFatalNetworkErrorCodes); - expect(networkConfig.getNonFatalErrorMessages()).andReturn(nonFatalNetworkErrorMessages); - - otherConfig = PowerMock.createMock(OtherConfig.class); - expect(otherConfig.getItem("buy-fee")).andReturn("0.2"); - expect(otherConfig.getItem("sell-fee")).andReturn("0.2"); - - exchangeConfig = PowerMock.createMock(ExchangeConfig.class); - expect(exchangeConfig.getAuthenticationConfig()).andReturn(authenticationConfig); - expect(exchangeConfig.getNetworkConfig()).andReturn(networkConfig); - expect(exchangeConfig.getOtherConfig()).andReturn(otherConfig); - } - - // -------------------------------------------------------------------------- - // Cancel Order tests - // -------------------------------------------------------------------------- - - @Test - @SuppressWarnings("unchecked") - public void testCancelOrderIsSuccessful() throws Exception { - // Load the canned response from the exchange - final byte[] encoded = Files.readAllBytes(Paths.get(CANCEL_ORDER_JSON_RESPONSE)); - final AbstractExchangeAdapter.ExchangeHttpResponse exchangeResponse = - new AbstractExchangeAdapter.ExchangeHttpResponse( - 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - - // Mock out param map so we can assert the contents passed to the transport layer are what we - // expect. - final Map requestParamMap = PowerMock.createMock(Map.class); - expect(requestParamMap.put("order_id", ORDER_ID_TO_CANCEL)).andStubReturn(null); - expect(requestParamMap.put("symbol", MARKET_ID)).andStubReturn(null); - - // Partial mock so we do not send stuff down the wire - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD); - - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD) - .andReturn(requestParamMap); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(CANCEL_ORDER), - eq(requestParamMap)) - .andReturn(exchangeResponse); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - final boolean success = exchangeAdapter.cancelOrder(ORDER_ID_TO_CANCEL, MARKET_ID); - assertTrue(success); - - PowerMock.verifyAll(); - } - - @Test - public void testCancelOrderExchangeErrorResponse() throws Exception { - final byte[] encoded = Files.readAllBytes(Paths.get(CANCEL_ORDER_ERROR_JSON_RESPONSE)); - final AbstractExchangeAdapter.ExchangeHttpResponse exchangeResponse = - new AbstractExchangeAdapter.ExchangeHttpResponse( - 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(CANCEL_ORDER), - anyObject(Map.class)) - .andReturn(exchangeResponse); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - assertFalse(exchangeAdapter.cancelOrder(ORDER_ID_TO_CANCEL, MARKET_ID)); - PowerMock.verifyAll(); - } - - @Test(expected = ExchangeNetworkException.class) - public void testCancelOrderHandlesExchangeNetworkException() throws Exception { - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(CANCEL_ORDER), - anyObject(Map.class)) - .andThrow( - new ExchangeNetworkException( - "I’ve thought of an ending for my book – “And he lived happily " - + "ever after… to the end of his days.")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.cancelOrder(ORDER_ID_TO_CANCEL, MARKET_ID); - PowerMock.verifyAll(); - } - - @Test(expected = TradingApiException.class) - public void testCancelOrderHandlesUnexpectedException() throws Exception { - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(CANCEL_ORDER), - anyObject(Map.class)) - .andThrow( - new IllegalStateException( - "A Balrog. A demon of the ancient world. This foe is beyond any of" - + " you. Run!")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.cancelOrder(ORDER_ID_TO_CANCEL, MARKET_ID); - PowerMock.verifyAll(); - } - - // -------------------------------------------------------------------------- - // Create Orders tests - // -------------------------------------------------------------------------- - - @Test - @SuppressWarnings("unchecked") - public void testCreateOrderToBuyIsSuccessful() throws Exception { - final byte[] encoded = Files.readAllBytes(Paths.get(TRADE_BUY_JSON_RESPONSE)); - final AbstractExchangeAdapter.ExchangeHttpResponse exchangeResponse = - new AbstractExchangeAdapter.ExchangeHttpResponse( - 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - - final Map requestParamMap = PowerMock.createMock(Map.class); - expect(requestParamMap.put("symbol", MARKET_ID)).andStubReturn(null); - expect( - requestParamMap.put( - "amount", - new DecimalFormat("#.########", getDecimalFormatSymbols()) - .format(BUY_ORDER_QUANTITY))) - .andStubReturn(null); - expect( - requestParamMap.put( - "price", - new DecimalFormat("#.########", getDecimalFormatSymbols()).format(BUY_ORDER_PRICE))) - .andStubReturn(null); - expect(requestParamMap.put("type", "buy")).andStubReturn(null); - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD); - - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD) - .andReturn(requestParamMap); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(TRADE), - eq(requestParamMap)) - .andReturn(exchangeResponse); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - final String orderId = - exchangeAdapter.createOrder(MARKET_ID, OrderType.BUY, BUY_ORDER_QUANTITY, BUY_ORDER_PRICE); - assertEquals("99646259", orderId); - - PowerMock.verifyAll(); - } - - @Test - @SuppressWarnings("unchecked") - public void testCreateOrderToSellIsSuccessful() throws Exception { - final byte[] encoded = Files.readAllBytes(Paths.get(TRADE_SELL_JSON_RESPONSE)); - final AbstractExchangeAdapter.ExchangeHttpResponse exchangeResponse = - new AbstractExchangeAdapter.ExchangeHttpResponse( - 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - - final Map requestParamMap = PowerMock.createMock(Map.class); - expect(requestParamMap.put("symbol", MARKET_ID)).andStubReturn(null); - expect( - requestParamMap.put( - "amount", - new DecimalFormat("#.########", getDecimalFormatSymbols()) - .format(SELL_ORDER_QUANTITY))) - .andStubReturn(null); - expect( - requestParamMap.put( - "price", - new DecimalFormat("#.########", getDecimalFormatSymbols()) - .format(SELL_ORDER_PRICE))) - .andStubReturn(null); - expect(requestParamMap.put("type", "sell")).andStubReturn(null); - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD); - - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD) - .andReturn(requestParamMap); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(TRADE), - eq(requestParamMap)) - .andReturn(exchangeResponse); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - final String orderId = - exchangeAdapter.createOrder( - MARKET_ID, OrderType.SELL, SELL_ORDER_QUANTITY, SELL_ORDER_PRICE); - assertEquals("99646257", orderId); - - PowerMock.verifyAll(); - } - - @Test(expected = TradingApiException.class) - public void testCreateOrderExchangeErrorResponse() throws Exception { - final byte[] encoded = Files.readAllBytes(Paths.get(TRADE_ERROR_JSON_RESPONSE)); - final AbstractExchangeAdapter.ExchangeHttpResponse exchangeResponse = - new AbstractExchangeAdapter.ExchangeHttpResponse( - 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(TRADE), - anyObject(Map.class)) - .andReturn(exchangeResponse); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.createOrder(MARKET_ID, OrderType.SELL, SELL_ORDER_QUANTITY, SELL_ORDER_PRICE); - PowerMock.verifyAll(); - } - - @Test(expected = ExchangeNetworkException.class) - public void testCreateOrderHandlesExchangeNetworkException() throws Exception { - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(TRADE), - anyObject(Map.class)) - .andThrow( - new ExchangeNetworkException( - "It’s like in the great stories, Mr. Frodo, the ones that really mattered. " - + "Full of darkness and danger, they were... Those were the stories " - + "that stayed with you, that meant something, even if you were too small to " - + "understand why. But I think, Mr. Frodo, I do understand... There’s some " - + "good in this world, Mr. Frodo, and it’s worth fighting for.")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.createOrder(MARKET_ID, OrderType.SELL, SELL_ORDER_QUANTITY, SELL_ORDER_PRICE); - PowerMock.verifyAll(); - } - - @Test(expected = TradingApiException.class) - public void testCreateOrderHandlesUnexpectedException() throws Exception { - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(TRADE), - anyObject(Map.class)) - .andThrow( - new IllegalArgumentException( - "We needs it. Must have the precious. They stole it from us. " - + "Sneaky little hobbitses, wicked, tricksy, false. No, not master... " - + "Master’s my friend. You don’t have any friends. Nobody likes you. " - + "Not listening. I’m not listening. You’re a liar. And a thief. Murderer. " - + "Go away... I hate you... Leave now and never come back.")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.createOrder(MARKET_ID, OrderType.BUY, BUY_ORDER_QUANTITY, BUY_ORDER_PRICE); - PowerMock.verifyAll(); - } - - // -------------------------------------------------------------------------- - // Get Your Open Orders tests - // -------------------------------------------------------------------------- - - @Test - @SuppressWarnings("unchecked") - public void testGettingYourOpenOrdersSuccessfully() throws Exception { - final byte[] encoded = Files.readAllBytes(Paths.get(ORDER_INFO_JSON_RESPONSE)); - final AbstractExchangeAdapter.ExchangeHttpResponse exchangeResponse = - new AbstractExchangeAdapter.ExchangeHttpResponse( - 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - - final Map requestParamMap = PowerMock.createMock(Map.class); - expect(requestParamMap.put("order_id", "-1")).andStubReturn(null); - expect(requestParamMap.put("symbol", MARKET_ID)).andStubReturn(null); - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD); - - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD) - .andReturn(requestParamMap); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(ORDER_INFO), - eq(requestParamMap)) - .andReturn(exchangeResponse); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - final List openOrders = exchangeAdapter.getYourOpenOrders(MARKET_ID); - - // assert some key stuff; we're not testing GSON here. - assertEquals(2, openOrders.size()); - assertEquals(MARKET_ID, openOrders.get(0).getMarketId()); - assertEquals("99031951", openOrders.get(0).getId()); - assertSame(OrderType.SELL, openOrders.get(0).getType()); - assertEquals(1442949893000L, openOrders.get(0).getCreationDate().getTime()); - assertEquals(0, openOrders.get(0).getPrice().compareTo(new BigDecimal("255"))); - assertEquals(0, openOrders.get(0).getQuantity().compareTo(new BigDecimal("0.015"))); - assertEquals( - 0, - openOrders - .get(0) - .getTotal() - .compareTo(openOrders.get(0).getPrice().multiply(openOrders.get(0).getQuantity()))); - - // the values below are not provided by OKCoin - assertNull(openOrders.get(0).getOriginalQuantity()); - - PowerMock.verifyAll(); - } - - @Test(expected = TradingApiException.class) - public void testGettingYourOpenOrdersExchangeErrorResponse() throws Exception { - final byte[] encoded = Files.readAllBytes(Paths.get(ORDER_INFO_ERROR_JSON_RESPONSE)); - final AbstractExchangeAdapter.ExchangeHttpResponse exchangeResponse = - new AbstractExchangeAdapter.ExchangeHttpResponse( - 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(ORDER_INFO), - anyObject(Map.class)) - .andReturn(exchangeResponse); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.getYourOpenOrders("junk_market_id"); - PowerMock.verifyAll(); - } - - @Test(expected = ExchangeNetworkException.class) - public void testGettingYourOpenOrdersHandlesExchangeNetworkException() throws Exception { - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(ORDER_INFO), - anyObject(Map.class)) - .andThrow( - new ExchangeNetworkException( - "If more of us valued food and cheer and" - + " song above hoarded gold, it would be a merrier world.")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.getYourOpenOrders(MARKET_ID); - PowerMock.verifyAll(); - } - - @Test(expected = TradingApiException.class) - public void testGettingYourOpenOrdersHandlesUnexpectedException() throws Exception { - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(ORDER_INFO), - anyObject(Map.class)) - .andThrow( - new IllegalStateException( - "The Road goes ever on and on\n" - + "Down from the door where it began.\n" - + "Now far ahead the Road has gone,\n" - + "And I must follow, if I can")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.getYourOpenOrders(MARKET_ID); - PowerMock.verifyAll(); - } - - // -------------------------------------------------------------------------- - // Get Market Orders tests - // -------------------------------------------------------------------------- - - @Test - @SuppressWarnings("unchecked") - public void testGettingMarketOrders() throws Exception { - final byte[] encoded = Files.readAllBytes(Paths.get(DEPTH_JSON_RESPONSE)); - final AbstractExchangeAdapter.ExchangeHttpResponse exchangeResponse = - new AbstractExchangeAdapter.ExchangeHttpResponse( - 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - - final Map requestParamMap = PowerMock.createMock(Map.class); - expect(requestParamMap.put("symbol", MARKET_ID)).andStubReturn(null); - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, - MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, - MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD); - - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD) - .andReturn(requestParamMap); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, - eq(DEPTH), - eq(requestParamMap)) - .andReturn(exchangeResponse); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - final MarketOrderBook marketOrderBook = exchangeAdapter.getMarketOrders(MARKET_ID); - - // assert some key stuff; we're not testing GSON here. - assertEquals(MARKET_ID, marketOrderBook.getMarketId()); - - final BigDecimal buyPrice = new BigDecimal("228.3"); - final BigDecimal buyQuantity = new BigDecimal("52.995"); - final BigDecimal buyTotal = buyPrice.multiply(buyQuantity); - - assertEquals(200, marketOrderBook.getBuyOrders().size()); - assertSame(OrderType.BUY, marketOrderBook.getBuyOrders().get(0).getType()); - assertEquals(0, marketOrderBook.getBuyOrders().get(0).getPrice().compareTo(buyPrice)); - assertEquals(0, marketOrderBook.getBuyOrders().get(0).getQuantity().compareTo(buyQuantity)); - assertEquals(0, marketOrderBook.getBuyOrders().get(0).getTotal().compareTo(buyTotal)); - - final BigDecimal sellPrice = new BigDecimal("228.36"); - final BigDecimal sellQuantity = new BigDecimal("0.01"); - final BigDecimal sellTotal = sellPrice.multiply(sellQuantity); - - assertEquals(200, marketOrderBook.getSellOrders().size()); - assertSame(OrderType.SELL, marketOrderBook.getSellOrders().get(0).getType()); - assertEquals(0, marketOrderBook.getSellOrders().get(0).getPrice().compareTo(sellPrice)); - assertEquals(0, marketOrderBook.getSellOrders().get(0).getQuantity().compareTo(sellQuantity)); - assertEquals(0, marketOrderBook.getSellOrders().get(0).getTotal().compareTo(sellTotal)); - - PowerMock.verifyAll(); - } - - @Test(expected = ExchangeNetworkException.class) - public void testGettingMarketOrdersHandlesExchangeNetworkException() throws Exception { - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, - eq(DEPTH), - anyObject(Map.class)) - .andThrow( - new ExchangeNetworkException( - "All we have to decide is what to do with the time that is given" + " to us.")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.getMarketOrders(MARKET_ID); - PowerMock.verifyAll(); - } - - @Test(expected = TradingApiException.class) - public void testGettingMarketOrdersHandlesUnexpectedException() throws Exception { - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, - eq(DEPTH), - anyObject(Map.class)) - .andThrow( - new IllegalArgumentException( - "The board is set, the pieces are moving. We come to it at last, " - + "the great battle of our time.")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.getMarketOrders(MARKET_ID); - PowerMock.verifyAll(); - } - - // -------------------------------------------------------------------------- - // Get Latest Market Price tests - // -------------------------------------------------------------------------- - - @Test - @SuppressWarnings("unchecked") - public void testGettingLatestMarketPriceSuccessfully() throws Exception { - final byte[] encoded = Files.readAllBytes(Paths.get(TICKER_JSON_RESPONSE)); - final AbstractExchangeAdapter.ExchangeHttpResponse exchangeResponse = - new AbstractExchangeAdapter.ExchangeHttpResponse( - 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - - final Map requestParamMap = PowerMock.createMock(Map.class); - expect(requestParamMap.put("symbol", MARKET_ID)).andStubReturn(null); - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, - MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, - MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD); - - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD) - .andReturn(requestParamMap); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, - eq(TICKER), - eq(requestParamMap)) - .andReturn(exchangeResponse); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - final BigDecimal latestMarketPrice = - exchangeAdapter.getLatestMarketPrice(MARKET_ID).setScale(8, RoundingMode.HALF_UP); - assertEquals(0, latestMarketPrice.compareTo(new BigDecimal("231.35"))); - PowerMock.verifyAll(); - } - - @Test(expected = ExchangeNetworkException.class) - public void testGettingLatestMarketPriceHandlesExchangeNetworkException() throws Exception { - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, - eq(TICKER), - anyObject(Map.class)) - .andThrow( - new ExchangeNetworkException( - "I would rather share one lifetime with you than face all the" - + " Ages of this world alone.")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.getLatestMarketPrice(MARKET_ID); - PowerMock.verifyAll(); - } - - @Test(expected = TradingApiException.class) - public void testGettingLatestMarketPriceHandlesUnexpectedException() throws Exception { - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, - eq(TICKER), - anyObject(Map.class)) - .andThrow( - new IllegalArgumentException( - "What has happened before will happen again. What has been done " - + "before will be done again. There is nothing new in the whole world. " - + "\"Look,\" they say, \"here is something new!\" But no, it has all happened " - + "before, long before we were born. No one remembers what has happened in the " - + "past, and no one in days to come will remember what happens between now " - + "and then.")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.getLatestMarketPrice(MARKET_ID); - PowerMock.verifyAll(); - } - - // -------------------------------------------------------------------------- - // Get Balance Info tests - // -------------------------------------------------------------------------- - - @Test - public void testGettingBalanceInfoSuccessfully() throws Exception { - final byte[] encoded = Files.readAllBytes(Paths.get(USERINFO_JSON_RESPONSE)); - final AbstractExchangeAdapter.ExchangeHttpResponse exchangeResponse = - new AbstractExchangeAdapter.ExchangeHttpResponse( - 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); - - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(USERINFO), - eq(null)) - .andReturn(exchangeResponse); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - final BalanceInfo balanceInfo = exchangeAdapter.getBalanceInfo(); - - // assert some key stuff; we're not testing GSON here. - assertEquals( - 0, balanceInfo.getBalancesAvailable().get("BTC").compareTo(new BigDecimal("0.06"))); - assertEquals( - 0, balanceInfo.getBalancesAvailable().get("USD").compareTo(new BigDecimal("0.0608"))); - - assertEquals(0, balanceInfo.getBalancesOnHold().get("BTC").compareTo(new BigDecimal("0.03"))); - assertEquals(0, balanceInfo.getBalancesOnHold().get("USD").compareTo(new BigDecimal("2.25"))); - - PowerMock.verifyAll(); - } - - @Test(expected = TradingApiException.class) - public void testGettingBalanceInfoExchangeErrorResponse() throws Exception { - final byte[] encoded = Files.readAllBytes(Paths.get(USERINFO_ERROR_JSON_RESPONSE)); - final AbstractExchangeAdapter.ExchangeHttpResponse exchangeResponse = - new AbstractExchangeAdapter.ExchangeHttpResponse( - 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(USERINFO), - anyObject(Map.class)) - .andReturn(exchangeResponse); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.getBalanceInfo(); - PowerMock.verifyAll(); - } - - @Test(expected = ExchangeNetworkException.class) - public void testGettingBalanceInfoHandlesExchangeNetworkException() throws Exception { - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(USERINFO), - eq(null)) - .andThrow( - new ExchangeNetworkException( - "There is only one Lord of the Ring, only one who can" - + " bend it to his will. And he does not share power.")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.getBalanceInfo(); - PowerMock.verifyAll(); - } - - @Test(expected = TradingApiException.class) - public void testGettingBalanceInfoHandlesUnexpectedException() throws Exception { - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_AUTHENTICATED_REQUEST_TO_EXCHANGE_METHOD, - eq(USERINFO), - eq(null)) - .andThrow( - new IllegalStateException( - "It's a dangerous business, Frodo, going out your door. You step onto the road, " - + "and if you don't keep your feet, there's no knowing where you might be " - + "swept off to.")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.getBalanceInfo(); - PowerMock.verifyAll(); - } - - // -------------------------------------------------------------------------- - // Get Ticker tests - // -------------------------------------------------------------------------- - - @Test - @SuppressWarnings("unchecked") - public void testGettingTickerSuccessfully() throws Exception { - final byte[] encoded = Files.readAllBytes(Paths.get(TICKER_JSON_RESPONSE)); - final AbstractExchangeAdapter.ExchangeHttpResponse exchangeResponse = - new AbstractExchangeAdapter.ExchangeHttpResponse( - 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - - final Map requestParamMap = PowerMock.createMock(Map.class); - expect(requestParamMap.put("symbol", MARKET_ID)).andStubReturn(null); - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, - MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, - MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD); - - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD) - .andReturn(requestParamMap); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, - eq(TICKER), - eq(requestParamMap)) - .andReturn(exchangeResponse); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - final Ticker ticker = exchangeAdapter.getTicker(MARKET_ID); - assertEquals(0, ticker.getLast().compareTo(new BigDecimal("231.35"))); - assertEquals(0, ticker.getAsk().compareTo(new BigDecimal("231.4"))); - assertEquals(0, ticker.getBid().compareTo(new BigDecimal("231.32"))); - assertEquals(0, ticker.getHigh().compareTo(new BigDecimal("233.6"))); - assertEquals(0, ticker.getLow().compareTo(new BigDecimal("231.01"))); - assertNull(ticker.getOpen()); // open not supplied by OKCoin - assertEquals(0, ticker.getVolume().compareTo(new BigDecimal("5465.046"))); - assertNull(ticker.getVwap()); // vwap not supplied by OKCoin - assertEquals(1442673698L, (long) ticker.getTimestamp()); - - PowerMock.verifyAll(); - } - - @Test(expected = ExchangeNetworkException.class) - public void testGettingTickerHandlesExchangeNetworkException() throws Exception { - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, - eq(TICKER), - anyObject(Map.class)) - .andThrow(new ExchangeNetworkException("Where the hell can I get eyes like that?")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.getTicker(MARKET_ID); - PowerMock.verifyAll(); - } - - @Test(expected = TradingApiException.class) - public void testGettingTickerHandlesUnexpectedException() throws Exception { - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_SEND_PUBLIC_REQUEST_TO_EXCHANGE_METHOD, - eq(TICKER), - anyObject(Map.class)) - .andThrow( - new IllegalArgumentException( - "All you people are so scared of me. Most days I'd take that as a compliment. " - + "But it ain't me you gotta worry about now.")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.getTicker(MARKET_ID); - PowerMock.verifyAll(); - } - - // -------------------------------------------------------------------------- - // Non Exchange visiting tests - // -------------------------------------------------------------------------- - - @Test - public void testGettingExchangeSellingFeeIsAsExpected() { - PowerMock.replayAll(); - - final OkCoinExchangeAdapter exchangeAdapter = new OkCoinExchangeAdapter(); - exchangeAdapter.init(exchangeConfig); - - final BigDecimal sellPercentageFee = - exchangeAdapter.getPercentageOfSellOrderTakenForExchangeFee(MARKET_ID); - assertEquals(0, sellPercentageFee.compareTo(new BigDecimal("0.002"))); - - PowerMock.verifyAll(); - } - - @Test - public void testGettingExchangeBuyingFeeIsAsExpected() { - PowerMock.replayAll(); - - final OkCoinExchangeAdapter exchangeAdapter = new OkCoinExchangeAdapter(); - exchangeAdapter.init(exchangeConfig); - - final BigDecimal buyPercentageFee = - exchangeAdapter.getPercentageOfBuyOrderTakenForExchangeFee(MARKET_ID); - assertEquals(0, buyPercentageFee.compareTo(new BigDecimal("0.002"))); - - PowerMock.verifyAll(); - } - - @Test - public void testGettingImplNameIsAsExpected() { - PowerMock.replayAll(); - - final OkCoinExchangeAdapter exchangeAdapter = new OkCoinExchangeAdapter(); - exchangeAdapter.init(exchangeConfig); - - assertEquals("OKCoin REST Spot Trading API v1", exchangeAdapter.getImplName()); - PowerMock.verifyAll(); - } - - // -------------------------------------------------------------------------- - // Initialisation tests - // -------------------------------------------------------------------------- - - @Test - public void testExchangeAdapterInitialisesSuccessfully() { - PowerMock.replayAll(); - final OkCoinExchangeAdapter exchangeAdapter = new OkCoinExchangeAdapter(); - exchangeAdapter.init(exchangeConfig); - assertNotNull(exchangeAdapter); - PowerMock.verifyAll(); - } - - @Test(expected = IllegalArgumentException.class) - public void testExchangeAdapterThrowsExceptionIfPublicKeyConfigIsMissing() { - PowerMock.reset(authenticationConfig); - expect(authenticationConfig.getItem("key")).andReturn(null); - expect(authenticationConfig.getItem("secret")).andReturn("your_client_secret"); - - PowerMock.replayAll(); - final OkCoinExchangeAdapter exchangeAdapter = new OkCoinExchangeAdapter(); - exchangeAdapter.init(exchangeConfig); - PowerMock.verifyAll(); - } - - @Test(expected = IllegalArgumentException.class) - public void testExchangeAdapterThrowsExceptionIfSecretConfigIsMissing() { - PowerMock.reset(authenticationConfig); - expect(authenticationConfig.getItem("key")).andReturn("your_client_key"); - expect(authenticationConfig.getItem("secret")).andReturn(null); - PowerMock.replayAll(); - - final OkCoinExchangeAdapter exchangeAdapter = new OkCoinExchangeAdapter(); - exchangeAdapter.init(exchangeConfig); - PowerMock.verifyAll(); - } - - @Test(expected = IllegalArgumentException.class) - public void testExchangeAdapterThrowsExceptionIfTimeoutConfigIsMissing() { - PowerMock.reset(networkConfig); - expect(networkConfig.getConnectionTimeout()).andReturn(0); - PowerMock.replayAll(); - - final OkCoinExchangeAdapter exchangeAdapter = new OkCoinExchangeAdapter(); - exchangeAdapter.init(exchangeConfig); - PowerMock.verifyAll(); - } - - @Test(expected = IllegalArgumentException.class) - public void testExchangeAdapterThrowsExceptionIfBuyFeeIsMissing() { - PowerMock.reset(otherConfig); - expect(otherConfig.getItem("buy-fee")).andReturn(""); - expect(otherConfig.getItem("sell-fee")).andReturn("0.2"); - PowerMock.replayAll(); - - final OkCoinExchangeAdapter exchangeAdapter = new OkCoinExchangeAdapter(); - exchangeAdapter.init(exchangeConfig); - PowerMock.verifyAll(); - } - - @Test(expected = IllegalArgumentException.class) - public void testExchangeAdapterThrowsExceptionIfSellFeeIsMissing() { - PowerMock.reset(otherConfig); - expect(otherConfig.getItem("buy-fee")).andReturn("0.2"); - expect(otherConfig.getItem("sell-fee")).andReturn(null); - PowerMock.replayAll(); - - final OkCoinExchangeAdapter exchangeAdapter = new OkCoinExchangeAdapter(); - exchangeAdapter.init(exchangeConfig); - PowerMock.verifyAll(); - } - - // -------------------------------------------------------------------------- - // Request sending tests - // -------------------------------------------------------------------------- - - @Test - @SuppressWarnings("unchecked") - public void testSendingPublicRequestToExchangeSuccessfully() throws Exception { - final byte[] encoded = Files.readAllBytes(Paths.get(TICKER_JSON_RESPONSE)); - final AbstractExchangeAdapter.ExchangeHttpResponse exchangeResponse = - new AbstractExchangeAdapter.ExchangeHttpResponse( - 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - - final Map requestParamMap = PowerMock.createPartialMock(HashMap.class, "put"); - expect(requestParamMap.put("symbol", MARKET_ID)).andStubReturn(null); - - final Map requestHeaderMap = PowerMock.createPartialMock(HashMap.class, "put"); - expect(requestHeaderMap.put("Content-Type", "application/x-www-form-urlencoded")) - .andStubReturn(null); - PowerMock.replay(requestHeaderMap); // map needs to be in play early - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, - MOCKED_MAKE_NETWORK_REQUEST_METHOD, - MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD, - MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD); - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD) - .andReturn(requestParamMap); - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD) - .andReturn(requestHeaderMap); - - final URL url = new URL(PUBLIC_API_BASE_URL + TICKER); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_MAKE_NETWORK_REQUEST_METHOD, - eq(url), - eq("GET"), - eq(null), - eq(requestHeaderMap)) - .andReturn(exchangeResponse); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - final BigDecimal lastMarketPrice = exchangeAdapter.getLatestMarketPrice(MARKET_ID); - assertEquals(0, lastMarketPrice.compareTo(new BigDecimal("231.35"))); - - PowerMock.verifyAll(); - } - - @Test(expected = ExchangeNetworkException.class) - @SuppressWarnings("unchecked") - public void testSendingPublicRequestToExchangeHandlesExchangeNetworkException() throws Exception { - final Map requestParamMap = PowerMock.createPartialMock(HashMap.class, "put"); - expect(requestParamMap.put("symbol", MARKET_ID)).andStubReturn(null); - - final Map requestHeaderMap = PowerMock.createPartialMock(HashMap.class, "put"); - expect(requestHeaderMap.put("Content-Type", "application/x-www-form-urlencoded")) - .andStubReturn(null); - PowerMock.replay(requestHeaderMap); // map needs to be in play early - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, - MOCKED_MAKE_NETWORK_REQUEST_METHOD, - MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD, - MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD); - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD) - .andReturn(requestParamMap); - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD) - .andReturn(requestHeaderMap); - - final URL url = new URL(PUBLIC_API_BASE_URL + TICKER); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_MAKE_NETWORK_REQUEST_METHOD, - eq(url), - eq("GET"), - eq(null), - eq(requestHeaderMap)) - .andThrow( - new ExchangeNetworkException( - "It's called a Zune. It's what everybody's listening to on Earth nowadays.")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.getLatestMarketPrice(MARKET_ID); - - PowerMock.verifyAll(); - } - - @Test(expected = TradingApiException.class) - @SuppressWarnings("unchecked") - public void testSendingPublicRequestToExchangeHandlesTradingApiException() throws Exception { - final Map requestParamMap = PowerMock.createPartialMock(HashMap.class, "put"); - expect(requestParamMap.put("symbol", MARKET_ID)).andStubReturn(null); - - final Map requestHeaderMap = PowerMock.createPartialMock(HashMap.class, "put"); - expect(requestHeaderMap.put("Content-Type", "application/x-www-form-urlencoded")) - .andStubReturn(null); - PowerMock.replay(requestHeaderMap); // map needs to be in play early - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, - MOCKED_MAKE_NETWORK_REQUEST_METHOD, - MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD, - MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD); - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD) - .andReturn(requestParamMap); - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD) - .andReturn(requestHeaderMap); - - final URL url = new URL(PUBLIC_API_BASE_URL + TICKER); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_MAKE_NETWORK_REQUEST_METHOD, - eq(url), - eq("GET"), - eq(null), - eq(requestHeaderMap)) - .andThrow(new TradingApiException("I am Groot.")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.getLatestMarketPrice(MARKET_ID); - - PowerMock.verifyAll(); - } - - @Test - @SuppressWarnings("unchecked") - public void testSendingAuthenticatedRequestToExchangeSuccessfully() throws Exception { - final byte[] encoded = Files.readAllBytes(Paths.get(TRADE_SELL_JSON_RESPONSE)); - final AbstractExchangeAdapter.ExchangeHttpResponse exchangeResponse = - new AbstractExchangeAdapter.ExchangeHttpResponse( - 200, "OK", new String(encoded, StandardCharsets.UTF_8)); - - final Map requestParamMap = PowerMock.createPartialMock(HashMap.class, "put"); - expect(requestParamMap.put("symbol", MARKET_ID)).andStubReturn(null); - expect( - requestParamMap.put( - "amount", - new DecimalFormat("#.########", getDecimalFormatSymbols()) - .format(SELL_ORDER_QUANTITY))) - .andStubReturn(null); - expect( - requestParamMap.put( - "price", - new DecimalFormat("#.########", getDecimalFormatSymbols()) - .format(SELL_ORDER_PRICE))) - .andStubReturn(null); - expect(requestParamMap.put("type", "sell")).andStubReturn(null); - expect(requestParamMap.put("api_key", KEY)).andStubReturn(null); - expect(requestParamMap.put(eq("sign"), anyString())).andStubReturn(null); - - final Map requestHeaderMap = PowerMock.createPartialMock(HashMap.class, "put"); - expect(requestHeaderMap.put("Content-Type", "application/x-www-form-urlencoded")) - .andStubReturn(null); - PowerMock.replay(requestHeaderMap); // map needs to be in play early - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, - MOCKED_MAKE_NETWORK_REQUEST_METHOD, - MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD, - MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD); - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD) - .andReturn(requestHeaderMap); - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD) - .andReturn(requestParamMap); - - final URL url = new URL(AUTHENTICATED_API_URL + TRADE); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_MAKE_NETWORK_REQUEST_METHOD, - eq(url), - eq("POST"), - anyString(), - eq(requestHeaderMap)) - .andReturn(exchangeResponse); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - final String orderId = - exchangeAdapter.createOrder( - MARKET_ID, OrderType.SELL, SELL_ORDER_QUANTITY, SELL_ORDER_PRICE); - assertEquals("99646257", orderId); - - PowerMock.verifyAll(); - } - - @Test(expected = ExchangeNetworkException.class) - @SuppressWarnings("unchecked") - public void testSendingAuthenticatedRequestToExchangeHandlesExchangeNetworkException() - throws Exception { - final Map requestParamMap = PowerMock.createPartialMock(HashMap.class, "put"); - expect(requestParamMap.put("symbol", MARKET_ID)).andStubReturn(null); - expect( - requestParamMap.put( - "amount", - new DecimalFormat("#.########", getDecimalFormatSymbols()) - .format(SELL_ORDER_QUANTITY))) - .andStubReturn(null); - expect( - requestParamMap.put( - "price", - new DecimalFormat("#.########", getDecimalFormatSymbols()) - .format(SELL_ORDER_PRICE))) - .andStubReturn(null); - expect(requestParamMap.put("type", "sell")).andStubReturn(null); - expect(requestParamMap.put("api_key", KEY)).andStubReturn(null); - expect(requestParamMap.put(eq("sign"), anyString())).andStubReturn(null); - - final Map requestHeaderMap = PowerMock.createPartialMock(HashMap.class, "put"); - expect(requestHeaderMap.put("Content-Type", "application/x-www-form-urlencoded")) - .andStubReturn(null); - PowerMock.replay(requestHeaderMap); // map needs to be in play early - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, - MOCKED_MAKE_NETWORK_REQUEST_METHOD, - MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD, - MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD); - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD) - .andReturn(requestHeaderMap); - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD) - .andReturn(requestParamMap); - - final URL url = new URL(AUTHENTICATED_API_URL + TRADE); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_MAKE_NETWORK_REQUEST_METHOD, - eq(url), - eq("POST"), - anyString(), - eq(requestHeaderMap)) - .andThrow(new ExchangeNetworkException("These aren’t the droids you’re looking for...")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.createOrder(MARKET_ID, OrderType.SELL, SELL_ORDER_QUANTITY, SELL_ORDER_PRICE); - - PowerMock.verifyAll(); - } - - @Test(expected = TradingApiException.class) - @SuppressWarnings("unchecked") - public void testSendingAuthenticatedRequestToExchangeHandlesTradingApiException() - throws Exception { - final Map requestParamMap = PowerMock.createPartialMock(HashMap.class, "put"); - expect(requestParamMap.put("symbol", MARKET_ID)).andStubReturn(null); - expect( - requestParamMap.put( - "amount", - new DecimalFormat("#.########", getDecimalFormatSymbols()) - .format(SELL_ORDER_QUANTITY))) - .andStubReturn(null); - expect( - requestParamMap.put( - "price", - new DecimalFormat("#.########", getDecimalFormatSymbols()) - .format(SELL_ORDER_PRICE))) - .andStubReturn(null); - expect(requestParamMap.put("type", "sell")).andStubReturn(null); - expect(requestParamMap.put("api_key", KEY)).andStubReturn(null); - expect(requestParamMap.put(eq("sign"), anyString())).andStubReturn(null); - - final Map requestHeaderMap = PowerMock.createPartialMock(HashMap.class, "put"); - expect(requestHeaderMap.put(eq("Content-Type"), eq("application/x-www-form-urlencoded"))) - .andStubReturn(null); - PowerMock.replay(requestHeaderMap); // map needs to be in play early - - final OkCoinExchangeAdapter exchangeAdapter = - PowerMock.createPartialMockAndInvokeDefaultConstructor( - OkCoinExchangeAdapter.class, - MOCKED_MAKE_NETWORK_REQUEST_METHOD, - MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD, - MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD); - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_HEADER_MAP_METHOD) - .andReturn(requestHeaderMap); - PowerMock.expectPrivate(exchangeAdapter, MOCKED_CREATE_REQUEST_PARAM_MAP_METHOD) - .andReturn(requestParamMap); - - final URL url = new URL(AUTHENTICATED_API_URL + TRADE); - PowerMock.expectPrivate( - exchangeAdapter, - MOCKED_MAKE_NETWORK_REQUEST_METHOD, - eq(url), - eq("POST"), - anyString(), - eq(requestHeaderMap)) - .andThrow( - new TradingApiException("I guess I prefer to make people the old-fashioned way.")); - - PowerMock.replayAll(); - exchangeAdapter.init(exchangeConfig); - - exchangeAdapter.createOrder(MARKET_ID, OrderType.SELL, SELL_ORDER_QUANTITY, SELL_ORDER_PRICE); - - PowerMock.verifyAll(); - } -} diff --git a/bxbot-repository/src/test/java/com/gazbert/bxbot/repository/yaml/TestMarketConfigYamlRepository.java b/bxbot-repository/src/test/java/com/gazbert/bxbot/repository/yaml/TestMarketConfigYamlRepository.java index 28cf96dfb..c59ba62b6 100644 --- a/bxbot-repository/src/test/java/com/gazbert/bxbot/repository/yaml/TestMarketConfigYamlRepository.java +++ b/bxbot-repository/src/test/java/com/gazbert/bxbot/repository/yaml/TestMarketConfigYamlRepository.java @@ -71,7 +71,7 @@ public class TestMarketConfigYamlRepository { private static final boolean MARKET_1_IS_ENABLED = true; private static final String MARKET_1_TRADING_STRATEGY_ID = "macd_trend_follower"; - private static final String MARKET_2_ID = "gdax_gbp/btc"; + private static final String MARKET_2_ID = "coinbasepro_gbp/btc"; private static final String MARKET_2_NAME = "BTC/GBP"; private static final String MARKET_2_BASE_CURRENCY = "BTC"; private static final String MARKET_2_COUNTER_CURRENCY = "GBP"; diff --git a/bxbot-yaml-datastore/src/test/java/com/gazbert/bxbot/datastore/yaml/market/TestMarketConfigurationManagement.java b/bxbot-yaml-datastore/src/test/java/com/gazbert/bxbot/datastore/yaml/market/TestMarketConfigurationManagement.java index 56d606cbe..3d320ab21 100644 --- a/bxbot-yaml-datastore/src/test/java/com/gazbert/bxbot/datastore/yaml/market/TestMarketConfigurationManagement.java +++ b/bxbot-yaml-datastore/src/test/java/com/gazbert/bxbot/datastore/yaml/market/TestMarketConfigurationManagement.java @@ -59,7 +59,7 @@ public class TestMarketConfigurationManagement { private static final boolean MARKET_1_IS_ENABLED = true; private static final String MARKET_1_TRADING_STRATEGY_ID = "macd_trend_follower"; - private static final String MARKET_2_ID = "gdax_gbp/btc"; + private static final String MARKET_2_ID = "coinbasepro_gbp/btc"; private static final String MARKET_2_NAME = "BTC/GBP"; private static final String MARKET_2_BASE_CURRENCY = "BTC"; private static final String MARKET_2_COUNTER_CURRENCY = "GBP"; diff --git a/config/samples/gdax/email-alerts.yaml b/config/samples/coinbase-pro/email-alerts.yaml similarity index 93% rename from config/samples/gdax/email-alerts.yaml rename to config/samples/coinbase-pro/email-alerts.yaml index aea86f6ef..55b896c69 100644 --- a/config/samples/gdax/email-alerts.yaml +++ b/config/samples/coinbase-pro/email-alerts.yaml @@ -1,8 +1,6 @@ ############################################################################################ # Email Alerts YAML config. # -# DO NOT USE: See https://github.com/gazbert/bxbot/pull/120 -# # - All fields are mandatory unless stated otherwise. # - Only 1 emailAlerts block can be specified. # - The email is sent using TLS. diff --git a/config/samples/gdax/engine.yaml b/config/samples/coinbase-pro/engine.yaml similarity index 88% rename from config/samples/gdax/engine.yaml rename to config/samples/coinbase-pro/engine.yaml index 27428551b..94731495a 100644 --- a/config/samples/gdax/engine.yaml +++ b/config/samples/coinbase-pro/engine.yaml @@ -1,8 +1,6 @@ ############################################################################################ # Trading Engine YAML config. # -# DO NOT USE: See https://github.com/gazbert/bxbot/pull/120 -# # - All fields are mandatory unless stated otherwise. # - Only 1 engine block can be specified. # - The indentation levels are significant in YAML: https://en.wikipedia.org/wiki/YAML @@ -11,11 +9,11 @@ engine: # A unique identifier for the bot. Value must be an alphanumeric string. - # Underscores and dashes are also permitted. E.g. my-gdax-bot_1 - botId: my-gdax-bot_1 + # Underscores and dashes are also permitted. + botId: my-coinbasepro-bot-1 - # A friendly name for the bot. Value must be an alphanumeric string. Spaces are allowed. E.g. GDAX Bot - botName: GDAX Bot + # A friendly name for the bot. Value must be an alphanumeric string. Spaces are allowed. + botName: CoinbasePro Bot # This must be set to prevent catastrophic loss on the exchange. # This is normally the currency you intend to hold a long position in. It should be set to the currency short code for the @@ -26,7 +24,7 @@ engine: # The Trading Engine checks this value at the start of every trade cycle: if your emergencyStopCurrency balance on # the trading drops below this value, the Trading Engine will stop trading on all markets and shutdown. # Manual intervention is then required to restart the bot. You can set this value to 0 to override this check. - emergencyStopBalance: 1.7 + emergencyStopBalance: 0.7 # The is the interval in seconds that the Trading Engine will wait/sleep before executing # the next trade cycle. The minimum value is 1 second. Some exchanges allow you to hit them harder than others. However, diff --git a/config/samples/gdax/exchange.yaml b/config/samples/coinbase-pro/exchange.yaml similarity index 70% rename from config/samples/gdax/exchange.yaml rename to config/samples/coinbase-pro/exchange.yaml index ecba04d5c..a0f1824c9 100644 --- a/config/samples/gdax/exchange.yaml +++ b/config/samples/coinbase-pro/exchange.yaml @@ -1,9 +1,7 @@ ############################################################################################ # Exchange Adapter YAML config. # -# DO NOT USE: See https://github.com/gazbert/bxbot/pull/120 -# -# - Sample config below currently set to run against GDAX (formerly Coinbase). +# - Sample config below currently set to run against Coinbase Pro # - All fields are mandatory unless stated otherwise. # - BX-bot only supports running 1 exchange per bot. # - The indentation levels are significant in YAML: https://en.wikipedia.org/wiki/YAML @@ -14,14 +12,14 @@ exchange: # A friendly name for the Exchange. Value must be an alphanumeric string. Spaces are allowed. - name>: GDAX + name>: Coinbase Pro # For the adapter value, you must specify the fully qualified name of your Exchange Adapter class so the Trading Engine # can load and execute it. The class must be on the runtime classpath. - adapter: com.gazbert.bxbot.exchanges.GdaxExchangeAdapter + adapter: com.gazbert.bxbot.exchanges.CoinbaseProExchangeAdapter authenticationConfig: - # See https://gdax.com/settings/api to get your GDAX Trading API credentials. + # See: https://docs.pro.coinbase.com/#authentication to get your Coinbase Pro Trading API credentials. passphrase: your-passphrase key: your-api-key secret: your-secret-key @@ -51,11 +49,18 @@ exchange: otherConfig: # Exchange Taker Buy fee in % - # IMPORTANT - keep an eye on the fees: https://docs.gdax.com/#fees - # Taker fee on 29 Jul 2016 = 0.25% - buy-fee: 0.25 + # IMPORTANT - keep an eye on the fees: + # https://help.coinbase.com/en/pro/trading-and-funding/trading-rules-and-fees/fees.html + buy-fee: 0.5 # Exchange Taker Sell fee in % - # IMPORTANT - keep an eye on the fees: https://docs.gdax.com/#fees - # Taker fee on 29 Jul 2016 = 0.25% - sell-fee: 0.25 + # IMPORTANT - keep an eye on the fees: + # https://help.coinbase.com/en/pro/trading-and-funding/trading-rules-and-fees/fees.html + sell-fee: 0.5 + + # Amount of time in seconds to add to the locally calculated timestamp used to sign the message + # sent to the exchange. This allows for slight skew between the bot's local time and that + # of the exchange. See: https://docs.pro.coinbase.com/#selecting-a-timestamp + # Start with 0 and see how you get on... + time-server-bias: 0 + diff --git a/config/samples/gdax/markets.yaml b/config/samples/coinbase-pro/markets.yaml similarity index 96% rename from config/samples/gdax/markets.yaml rename to config/samples/coinbase-pro/markets.yaml index 5d300511b..990f8c1eb 100644 --- a/config/samples/gdax/markets.yaml +++ b/config/samples/coinbase-pro/markets.yaml @@ -1,8 +1,6 @@ ############################################################################################ # Market YAML config. # -# DO NOT USE: See https://github.com/gazbert/bxbot/pull/120 -# # - All fields are mandatory unless stated otherwise. # - Multiple market blocks can be listed. # - The indentation levels are significant in YAML: https://en.wikipedia.org/wiki/YAML diff --git a/config/samples/gdax/strategies.yaml b/config/samples/coinbase-pro/strategies.yaml similarity index 97% rename from config/samples/gdax/strategies.yaml rename to config/samples/coinbase-pro/strategies.yaml index 9e532c766..aa953259d 100644 --- a/config/samples/gdax/strategies.yaml +++ b/config/samples/coinbase-pro/strategies.yaml @@ -1,8 +1,6 @@ ############################################################################################ # Trading Strategy YAML config. # -# DO NOT USE: See https://github.com/gazbert/bxbot/pull/120 -# # - You configure the loading of your strategy using either a className or a beanName field. # - All fields are mandatory unless stated otherwise. # - Multiple strategy blocks can be listed. diff --git a/config/samples/okcoin/email-alerts.yaml b/config/samples/okcoin/email-alerts.yaml deleted file mode 100644 index aaa059fc6..000000000 --- a/config/samples/okcoin/email-alerts.yaml +++ /dev/null @@ -1,27 +0,0 @@ -############################################################################################ -# Email Alerts YAML config. -# -# DO NOT USE: See https://github.com/gazbert/bxbot/issues/122 -# -# - All fields are mandatory unless stated otherwise. -# - Only 1 emailAlerts block can be specified. -# - The email is sent using TLS. -# - The indentation levels are significant in YAML: https://en.wikipedia.org/wiki/YAML -# -# Sample config for using a Gmail account to send the email is shown below. -############################################################################################ ---- -emailAlerts: - - # If set to true, the bot will load the smtpConfig, and enable email alerts. - enabled: false - - # Set your SMTP details here. - smtpConfig: - host: smtp.gmail.com - tlsPort: 587 - accountUsername: your.account.username@gmail.com - accountPassword: your.account.password - fromAddress: from.addr@gmail.com - toAddress: to.addr@gmail.com - diff --git a/config/samples/okcoin/engine.yaml b/config/samples/okcoin/engine.yaml deleted file mode 100644 index 5965476df..000000000 --- a/config/samples/okcoin/engine.yaml +++ /dev/null @@ -1,36 +0,0 @@ -############################################################################################ -# Trading Engine YAML config. -# -# DO NOT USE: See https://github.com/gazbert/bxbot/issues/122 -# -# - All fields are mandatory unless stated otherwise. -# - Only 1 engine block can be specified. -# - The indentation levels are significant in YAML: https://en.wikipedia.org/wiki/YAML -############################################################################################ ---- -engine: - - # A unique identifier for the bot. Value must be an alphanumeric string. - # Underscores and dashes are also permitted. E.g. my-okcoin-bot_1 - botId: my-okcoin-bot_1 - - # A friendly name for the bot. Value must be an alphanumeric string. Spaces are allowed. E.g. OKCoin Bot - botName: OKCoin Bot - - # This must be set to prevent catastrophic loss on the exchange. - # This is normally the currency you intend to hold a long position in. It should be set to the currency short code for the - # wallet, e.g. BTC, LTC, USD. This value can be case sensitive for some exchanges - check the Exchange Adapter documentation. - emergencyStopCurrency: USD - - # This must be set to prevent a catastrophic loss on the exchange. - # The Trading Engine checks this value at the start of every trade cycle: if your emergencyStopCurrency balance on - # the trading drops below this value, the Trading Engine will stop trading on all markets and shutdown. - # Manual intervention is then required to restart the bot. You can set this value to 0 to override this check. - emergencyStopBalance: 50 - - # The is the interval in seconds that the Trading Engine will wait/sleep before executing - # the next trade cycle. The minimum value is 1 second. Some exchanges allow you to hit them harder than others. However, - # while their API documentation might say one thing, the reality is you might get socket timeouts and 5XX responses if you - # hit it too hard - you cannot perform ultra low latency trading over the public internet ;-) - # You'll need to experiment with the trade cycle interval for different exchanges. - tradeCycleInterval: 60 \ No newline at end of file diff --git a/config/samples/okcoin/exchange.yaml b/config/samples/okcoin/exchange.yaml deleted file mode 100644 index 99614395d..000000000 --- a/config/samples/okcoin/exchange.yaml +++ /dev/null @@ -1,60 +0,0 @@ -############################################################################################ -# Exchange Adapter YAML config. -# -# DO NOT USE: See https://github.com/gazbert/bxbot/issues/122 -# -# - Sample config below currently set to run against OKCoin. -# - All fields are mandatory unless stated otherwise. -# - BX-bot only supports running 1 exchange per bot. -# - The indentation levels are significant in YAML: https://en.wikipedia.org/wiki/YAML -# -# See the README "How do I write my own Exchange Adapter?" section for more details. -############################################################################################ ---- -exchange: - - # A friendly name for the Exchange. Value must be an alphanumeric string. Spaces are allowed. - name: OKCoin - - # For the adapter value, you must specify the fully qualified name of your Exchange Adapter class so the Trading Engine - # can load and execute it. The class must be on the runtime classpath. - adapter: com.gazbert.bxbot.exchanges.OkCoinExchangeAdapter - - authenticationConfig: - # See https://www.okcoin.com/user/api.do to get your OKCoin Trading API credentials. - key: your-api-key - secret: your-secret-key - - networkConfig: - # This value is in SECONDS. It is the timeout value that the exchange adapter will wait on socket connect/socket read - # when communicating with the exchange. Once this threshold has been breached, the exchange adapter will give up and - # throw a Trading API TimeoutException. - # - # The exchange adapter is single threaded: if one request gets blocked, it will block all subsequent requests from - # getting to the exchange. This timeout prevents an indefinite block. - # - # You'll need to experiment with values here. - connectionTimeout: 30 - - # Optional HTTP status codes that will trigger the adapter to throw a non-fatal ExchangeNetworkException - # if the exchange returns any of the below in an API call response: - nonFatalErrorCodes: [502, 503, 504, 520, 522, 525] - - # Optional java.io exception messages that will trigger the adapter to throw a non-fatal ExchangeNetworkException - # if the exchange returns any of the below in an API call response: - nonFatalErrorMessages: - - Connection reset - - Connection refused - - Remote host closed connection during handshake - - Unexpected end of file from server - - otherConfig: - # Exchange Taker Buy fee in % - # IMPORTANT - keep an eye on the fees: https://www.okcoin.com/about/fees.do - # Taker fee on 29 Jul 2016 = 0.2% - buy-fee: 0.2 - - # Exchange Taker Sell fee in % - # IMPORTANT - keep an eye on the fees: https://www.okcoin.com/about/fees.do - # Taker fee on 29 Jul 2016 = 0.2% - sell-fee: 0.2 \ No newline at end of file diff --git a/config/samples/okcoin/markets.yaml b/config/samples/okcoin/markets.yaml deleted file mode 100644 index 94285d76f..000000000 --- a/config/samples/okcoin/markets.yaml +++ /dev/null @@ -1,35 +0,0 @@ -############################################################################################ -# Market YAML config. -# -# DO NOT USE: See https://github.com/gazbert/bxbot/issues/122 -# -# - All fields are mandatory unless stated otherwise. -# - Multiple market blocks can be listed. -# - The indentation levels are significant in YAML: https://en.wikipedia.org/wiki/YAML -############################################################################################ ---- -markets: - - # The id value is the market id as defined on the exchange, e.g. 'btc_usd'. - - id: btc_usd - - # A friendly name for the market. - # Value must be an alphanumeric string. Spaces are allowed. E.g. BTC/USD - name: BTC/USD - - # The baseCurrency value is the currency short code for the base currency in the currency pair. When you buy or sell a - # currency pair, you are performing that action on the base currency. The base currency is the commodity you are buying or - # selling. E.g. in a BTC/USD market, the first currency (BTC) is the base currency and the second currency (USD) is the - # counter currency. - baseCurrency: BTC - - # The counterCurrency value is the currency short code for the counter currency in the currency pair. This is also known - # as the quote currency. - counterCurrency: USD - - # The enabled value allows you toggle trading on the market - config changes are only applied on startup. - enabled: true - - # The tradingStrategyId value must match a strategy id defined in your strategies.yaml config. - # Currently, BX-bot only supports 1 strategy per market. - tradingStrategyId: scalping-strategy diff --git a/config/samples/okcoin/strategies.yaml b/config/samples/okcoin/strategies.yaml deleted file mode 100644 index 0fbea031a..000000000 --- a/config/samples/okcoin/strategies.yaml +++ /dev/null @@ -1,47 +0,0 @@ -############################################################################################ -# Trading Strategy YAML config. -# -# DO NOT USE: See https://github.com/gazbert/bxbot/issues/122 -# -# - You configure the loading of your strategy using either a className or a beanName field. -# - All fields are mandatory unless stated otherwise. -# - Multiple strategy blocks can be listed. -# - The indentation levels are significant in YAML: https://en.wikipedia.org/wiki/YAML -# -# See the README "How do I write my own Trading Strategy?" section for full details. -############################################################################################ ---- -strategies: - - # A unique identifier for the strategy. The markets.yaml tradingStrategyId entries reference this. - # Value must be an alphanumeric string. Underscores and dashes are also permitted. E.g. my-macd-strat-1 - - id: scalping-strategy - - # A friendly name for the strategy. - # Value must be an alphanumeric string. Spaces are allowed. E.g. My Super MACD Strat - name: Basic Scalping Strat - - # The description value is optional. - description: > - A simple trend following scalper that buys at the current BID price, holds until current market price has reached - a configurable minimum percentage gain, and then sells at current ASK price, thereby taking profit from the spread. - Don't forget to factor in the exchange fees! - - # For the className value, you must specify the fully qualified name of your Strategy class for the - # Trading Engine to load and execute. This class must be on the runtime classpath. - # If you set this value to load your strategy, you cannot set the beanName value. - className: com.gazbert.bxbot.strategies.ExampleScalpingStrategy - - # For the beanName value, you must specify the Spring bean name of you Strategy component class - # for the Trading Engine to load and execute. - # You will also need to annotate your strategy class with `@Component("exampleScalpingStrategy")` - - # take a look at ExampleScalpingStrategy.java. This results in Spring injecting the bean. - # (see https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Component.html) - # If you set this value to load your strategy, you cannot set the className value. - #beanName: exampleScalpingStrategy - - # The configItems section is optional and allows you to set custom key/value pair config items. This config - # is passed to your Trading Strategy when the bot starts up. - configItems: - counter-currency-buy-order-amount: 20 - minimum-percentage-gain: 2 diff --git a/etc/spotbugs-exclude-filter.xml b/etc/spotbugs-exclude-filter.xml index 4d7664843..ff7aeb7fb 100644 --- a/etc/spotbugs-exclude-filter.xml +++ b/etc/spotbugs-exclude-filter.xml @@ -46,18 +46,6 @@ - - - - - - - - - - - - @@ -70,19 +58,17 @@ - + - - + + - - + @@ -102,11 +88,11 @@ - - +