diff --git a/README.md b/README.md
index 69c2608f4..d4be60b44 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# BX-bot
-[](https://travis-ci.com/gazbert/bxbot)
+[](https://travis-ci.com/gazbert/bxbot)
[](https://sonarcloud.io/dashboard?id=gazbert_bxbot)
[](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 @@
-
-
+