/******************************************************************************* * * Copyright (C) 2015-2018 Jan Kristof Nidzwetzki * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *******************************************************************************/ package com.github.jnidzwetzki.bitfinex.v2.test.integration; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.BiConsumer; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import com.github.jnidzwetzki.bitfinex.v2.BitfinexApiCallbackRegistry; import com.github.jnidzwetzki.bitfinex.v2.BitfinexConnectionFeature; import com.github.jnidzwetzki.bitfinex.v2.BitfinexWebsocketClient; import com.github.jnidzwetzki.bitfinex.v2.BitfinexWebsocketConfiguration; import com.github.jnidzwetzki.bitfinex.v2.SequenceNumberAuditor; import com.github.jnidzwetzki.bitfinex.v2.SimpleBitfinexApiBroker; import com.github.jnidzwetzki.bitfinex.v2.entity.BitfinexCandle; import com.github.jnidzwetzki.bitfinex.v2.entity.BitfinexCandleTimeFrame; import com.github.jnidzwetzki.bitfinex.v2.entity.BitfinexExecutedTrade; import com.github.jnidzwetzki.bitfinex.v2.entity.BitfinexOrderBookEntry; import com.github.jnidzwetzki.bitfinex.v2.entity.BitfinexTick; import com.github.jnidzwetzki.bitfinex.v2.entity.currency.BitfinexCurrencyPair; import com.github.jnidzwetzki.bitfinex.v2.exception.BitfinexClientException; import com.github.jnidzwetzki.bitfinex.v2.manager.ConnectionFeatureManager; import com.github.jnidzwetzki.bitfinex.v2.manager.FutureOperation; import com.github.jnidzwetzki.bitfinex.v2.manager.OrderbookManager; import com.github.jnidzwetzki.bitfinex.v2.manager.QuoteManager; import com.github.jnidzwetzki.bitfinex.v2.manager.RawOrderbookManager; import com.github.jnidzwetzki.bitfinex.v2.symbol.BitfinexCandlestickSymbol; import com.github.jnidzwetzki.bitfinex.v2.symbol.BitfinexExecutedTradeSymbol; import com.github.jnidzwetzki.bitfinex.v2.symbol.BitfinexOrderBookSymbol; import com.github.jnidzwetzki.bitfinex.v2.symbol.BitfinexSymbols; import com.github.jnidzwetzki.bitfinex.v2.symbol.BitfinexTickerSymbol; public class IntegrationTest { @BeforeClass public static void registerDefaultCurrencyPairs() { if(BitfinexCurrencyPair.values().size() < 10) { BitfinexCurrencyPair.unregisterAll(); BitfinexCurrencyPair.registerDefaults(); } } /** * Try to fetch wallets on an unauthenticated connection */ @Test public void testWalletsOnUnauthClient() throws BitfinexClientException { final BitfinexWebsocketClient bitfinexClient = new SimpleBitfinexApiBroker(new BitfinexWebsocketConfiguration(), new BitfinexApiCallbackRegistry(), new SequenceNumberAuditor(), false); try { bitfinexClient.connect(); Assert.assertFalse(bitfinexClient.isAuthenticated()); try { bitfinexClient.getWalletManager().getWallets(); // Should not happen Assert.fail(); } catch (BitfinexClientException e) { return; } } catch (Exception e) { // Should not happen e.printStackTrace(); Assert.fail(); } finally { bitfinexClient.close(); } } /** * Test the orderbook stream */ @Test(timeout=30000) public void testOrderbookStream() { final BitfinexWebsocketClient bitfinexClient = new SimpleBitfinexApiBroker(new BitfinexWebsocketConfiguration(), new BitfinexApiCallbackRegistry(), new SequenceNumberAuditor(), false); // Await at least 10 callbacks final CountDownLatch latch = new CountDownLatch(10); try { bitfinexClient.connect(); final BitfinexOrderBookSymbol orderbookConfiguration = BitfinexSymbols.orderBook( BitfinexCurrencyPair.of("BTC","USD"), BitfinexOrderBookSymbol.Precision.P0, BitfinexOrderBookSymbol.Frequency.F0, 25); final OrderbookManager orderbookManager = bitfinexClient.getOrderbookManager(); final BiConsumer<BitfinexOrderBookSymbol, BitfinexOrderBookEntry> callback = (c, o) -> { Assert.assertTrue(o.getAmount().doubleValue() != 0); Assert.assertTrue(o.getPrice().doubleValue() != 0); Assert.assertTrue(o.getCount().doubleValue() != 0); Assert.assertTrue(o.toString().length() > 0); latch.countDown(); }; orderbookManager.registerOrderbookCallback(orderbookConfiguration, callback); orderbookManager.subscribeOrderbook(orderbookConfiguration); latch.await(); orderbookManager.unsubscribeOrderbook(orderbookConfiguration); Assert.assertTrue(orderbookManager.removeOrderbookCallback(orderbookConfiguration, callback)); Assert.assertFalse(orderbookManager.removeOrderbookCallback(orderbookConfiguration, callback)); } catch (Exception e) { // Should not happen e.printStackTrace(); Assert.fail(); } finally { bitfinexClient.close(); } } /** * Test the raw orderbook stream */ @Test(timeout=30000) public void testRawOrderbookStream() { final BitfinexWebsocketClient bitfinexClient = new SimpleBitfinexApiBroker(new BitfinexWebsocketConfiguration(), new BitfinexApiCallbackRegistry(), new SequenceNumberAuditor(), false); // Await at least 20 callbacks final CountDownLatch latch = new CountDownLatch(20); try { bitfinexClient.connect(); BitfinexOrderBookSymbol orderbookConfiguration = BitfinexSymbols.rawOrderBook(BitfinexCurrencyPair.of("BTC", "USD")); final RawOrderbookManager rawOrderbookManager = bitfinexClient.getRawOrderbookManager(); final BiConsumer<BitfinexOrderBookSymbol, BitfinexOrderBookEntry> callback = (c, o) -> { Assert.assertTrue(o.getAmount().doubleValue() != 0); Assert.assertTrue(o.getPrice().doubleValue() != 0); Assert.assertTrue(o.getOrderId() >= 0); Assert.assertTrue(o.toString().length() > 0); latch.countDown(); }; rawOrderbookManager.registerOrderbookCallback(orderbookConfiguration, callback); rawOrderbookManager.subscribeOrderbook(orderbookConfiguration); latch.await(); rawOrderbookManager.unsubscribeOrderbook(orderbookConfiguration); Assert.assertTrue(rawOrderbookManager.removeOrderbookCallback(orderbookConfiguration, callback)); Assert.assertFalse(rawOrderbookManager.removeOrderbookCallback(orderbookConfiguration, callback)); } catch (Exception e) { // Should not happen e.printStackTrace(); Assert.fail(); } finally { bitfinexClient.close(); } } /** * Test the candle stream */ @Test(timeout=30000) public void testCandleStream() { final BitfinexWebsocketClient bitfinexClient = new SimpleBitfinexApiBroker(new BitfinexWebsocketConfiguration(), new BitfinexApiCallbackRegistry(), new SequenceNumberAuditor(), false); try { bitfinexClient.connect(); final List<BitfinexCandlestickSymbol> symbols = Arrays.asList( BitfinexSymbols.candlesticks(BitfinexCurrencyPair.of("BTC","USD"), BitfinexCandleTimeFrame.MINUTES_1), BitfinexSymbols.candlesticks(BitfinexCurrencyPair.of("BTC","USD"), BitfinexCandleTimeFrame.DAY_1), BitfinexSymbols.candlesticks(BitfinexCurrencyPair.of("BTC","USD"), BitfinexCandleTimeFrame.MONTH_1) ); final QuoteManager orderbookManager = bitfinexClient.getQuoteManager(); for(final BitfinexCandlestickSymbol symbol : symbols) { // Await at least 10 callbacks final CountDownLatch latch1 = new CountDownLatch(10); final BiConsumer<BitfinexCandlestickSymbol, BitfinexCandle> callback = (c, o) -> { latch1.countDown(); }; orderbookManager.registerCandlestickCallback(symbol, callback); orderbookManager.subscribeCandles(symbol); latch1.await(); orderbookManager.unsubscribeCandles(symbol); Assert.assertTrue(orderbookManager.removeCandlestickCallback(symbol, callback)); Assert.assertFalse(orderbookManager.removeCandlestickCallback(symbol, callback)); } } catch (Exception e) { // Should not happen e.printStackTrace(); Assert.fail(); } finally { bitfinexClient.close(); } } /** * Test executed trades stream */ @Test(timeout=60000) public void testExecutedTradesStream() { final BitfinexWebsocketClient bitfinexClient = new SimpleBitfinexApiBroker(new BitfinexWebsocketConfiguration(), new BitfinexApiCallbackRegistry(), new SequenceNumberAuditor(), false); // Await at least 2 callbacks final CountDownLatch latch = new CountDownLatch(2); try { bitfinexClient.connect(); final BitfinexExecutedTradeSymbol symbol = BitfinexSymbols.executedTrades(BitfinexCurrencyPair.of("BTC","USD")); final QuoteManager executedTradeManager = bitfinexClient.getQuoteManager(); final BiConsumer<BitfinexExecutedTradeSymbol, BitfinexExecutedTrade> callback = (c, o) -> { latch.countDown(); }; executedTradeManager.registerExecutedTradeCallback(symbol, callback); executedTradeManager.subscribeExecutedTrades(symbol); latch.await(); executedTradeManager.unsubscribeExecutedTrades(symbol); Assert.assertTrue(executedTradeManager.removeExecutedTradeCallback(symbol, callback)); Assert.assertFalse(executedTradeManager.removeExecutedTradeCallback(symbol, callback)); } catch (Exception e) { // Should not happen e.printStackTrace(); Assert.fail(); } finally { bitfinexClient.close(); } } /** * Test unsubscribe all channels */ @Test(timeout=60000) public void testUnsubscrribeAllChannels() { final BitfinexWebsocketClient bitfinexClient = new SimpleBitfinexApiBroker(new BitfinexWebsocketConfiguration(), new BitfinexApiCallbackRegistry(), new SequenceNumberAuditor(), false); try { bitfinexClient.connect(); final BitfinexExecutedTradeSymbol symbol1 = BitfinexSymbols.executedTrades(BitfinexCurrencyPair.of("BTC","USD")); final BitfinexExecutedTradeSymbol symbol2 = BitfinexSymbols.executedTrades(BitfinexCurrencyPair.of("ETH","USD")); final QuoteManager quoteManager = bitfinexClient.getQuoteManager(); final FutureOperation subscribe1 = quoteManager.subscribeExecutedTrades(symbol1); final FutureOperation subscribe2 = quoteManager.subscribeExecutedTrades(symbol2); subscribe1.waitForCompletion(); subscribe2.waitForCompletion(); Assert.assertEquals(2, bitfinexClient.getSubscribedChannels().size()); bitfinexClient.unsubscribeAllChannels(); Assert.assertTrue(bitfinexClient.getSubscribedChannels().isEmpty()); } catch (Exception e) { // Should not happen e.printStackTrace(); Assert.fail(); } finally { bitfinexClient.close(); } } /** * Test the tick stream */ @Test(timeout=120_000) public void testTickerStream() { final BitfinexWebsocketClient bitfinexClient = new SimpleBitfinexApiBroker(new BitfinexWebsocketConfiguration(), new BitfinexApiCallbackRegistry(), new SequenceNumberAuditor(), false); // Await at least 2 callbacks final CountDownLatch latch = new CountDownLatch(2); try { bitfinexClient.connect(); final BitfinexTickerSymbol symbol = BitfinexSymbols.ticker(BitfinexCurrencyPair.of("BTC","USD")); final QuoteManager orderbookManager = bitfinexClient.getQuoteManager(); final BiConsumer<BitfinexTickerSymbol, BitfinexTick> callback = (c, o) -> { latch.countDown(); }; orderbookManager.registerTickCallback(symbol, callback); orderbookManager.subscribeTicker(symbol); latch.await(); Assert.assertTrue(bitfinexClient.getSubscribedChannels().contains(symbol)); final FutureOperation unsubscribeFuture = orderbookManager.unsubscribeTicker(symbol); unsubscribeFuture.waitForCompletion(); Assert.assertFalse(bitfinexClient.getSubscribedChannels().contains(symbol)); Assert.assertTrue(orderbookManager.removeTickCallback(symbol, callback)); Assert.assertFalse(orderbookManager.removeTickCallback(symbol, callback)); } catch (Exception e) { // Should not happen e.printStackTrace(); Assert.fail(); } finally { bitfinexClient.close(); } } /** * Test auth failed * @throws BitfinexClientException */ @Test(expected=BitfinexClientException.class, timeout=120_000) public void testAuthFailed() throws BitfinexClientException { final String KEY = "key"; final String SECRET = "secret"; BitfinexWebsocketConfiguration config = new BitfinexWebsocketConfiguration(); config.setApiCredentials(KEY, SECRET); final BitfinexWebsocketClient bitfinexClient = new SimpleBitfinexApiBroker(config, new BitfinexApiCallbackRegistry(), new SequenceNumberAuditor(), false); Assert.assertEquals(KEY, bitfinexClient.getConfiguration().getApiKey()); Assert.assertEquals(SECRET, bitfinexClient.getConfiguration().getApiSecret()); Assert.assertFalse(bitfinexClient.isAuthenticated()); bitfinexClient.connect(); // Should not be reached Assert.fail(); bitfinexClient.close(); } /** * Test the session reconnect * @throws BitfinexClientException * @throws InterruptedException * @throws ExecutionException */ @Test(timeout=600_000) public void testReconnect() throws BitfinexClientException, InterruptedException, ExecutionException { final BitfinexWebsocketClient bitfinexClient = new SimpleBitfinexApiBroker(new BitfinexWebsocketConfiguration(), new BitfinexApiCallbackRegistry(), new SequenceNumberAuditor(), false); bitfinexClient.connect(); final BitfinexTickerSymbol symbol = BitfinexSymbols.ticker(BitfinexCurrencyPair.of("BTC","USD")); final QuoteManager orderbookManager = bitfinexClient.getQuoteManager(); final FutureOperation subscribeFuture = orderbookManager.subscribeTicker(symbol); subscribeFuture.waitForCompletion(); // Await at least 2 callbacks final CountDownLatch latchBefore = new CountDownLatch(2); final BiConsumer<BitfinexTickerSymbol, BitfinexTick> beforeCallback = (c, o) -> { latchBefore.countDown(); }; orderbookManager.registerTickCallback(symbol, beforeCallback); latchBefore.await(); orderbookManager.removeTickCallback(symbol, beforeCallback); final boolean reconnectResult = bitfinexClient.reconnect(); Assert.assertTrue(reconnectResult); // Await at least 2 callbacks final CountDownLatch latch = new CountDownLatch(2); final BiConsumer<BitfinexTickerSymbol, BitfinexTick> callback = (c, o) -> { latch.countDown(); }; orderbookManager.registerTickCallback(symbol, callback); latch.await(); Assert.assertTrue(bitfinexClient.getSubscribedChannels().contains(symbol)); final FutureOperation unsubscribeFuture = orderbookManager.unsubscribeTicker(symbol); unsubscribeFuture.waitForCompletion(); Assert.assertFalse(bitfinexClient.getSubscribedChannels().contains(symbol)); bitfinexClient.close(); } /** * Test the sequencing feature * @throws BitfinexClientException * @throws InterruptedException * @throws TimeoutException * @throws ExecutionException */ @Test public void testSequencing() throws BitfinexClientException, InterruptedException, ExecutionException, TimeoutException { final SequenceNumberAuditor sequenceNumberAuditor = new SequenceNumberAuditor(); final BitfinexWebsocketClient bitfinexClient = new SimpleBitfinexApiBroker(new BitfinexWebsocketConfiguration(), new BitfinexApiCallbackRegistry(), sequenceNumberAuditor, false); bitfinexClient.connect(); Assert.assertEquals(-1, sequenceNumberAuditor.getPrivateSequence()); Assert.assertEquals(-1, sequenceNumberAuditor.getPublicSequence()); Assert.assertEquals(SequenceNumberAuditor.ErrorPolicy.LOG_ONLY, sequenceNumberAuditor.getErrorPolicy()); final ConnectionFeatureManager cfManager = bitfinexClient.getConnectionFeatureManager(); Assert.assertEquals(0, cfManager.getActiveConnectionFeatures()); Assert.assertFalse(cfManager.isConnectionFeatureEnabled(BitfinexConnectionFeature.SEQ_ALL)); cfManager.enableConnectionFeature(BitfinexConnectionFeature.SEQ_ALL); Thread.sleep(1000); Assert.assertTrue(cfManager.isConnectionFeatureActive(BitfinexConnectionFeature.SEQ_ALL)); Assert.assertEquals(BitfinexConnectionFeature.SEQ_ALL.getFeatureFlag(), cfManager.getActiveConnectionFeatures()); // Register some ticket to get some sequence numbers final BitfinexTickerSymbol symbol1 = BitfinexSymbols.ticker(BitfinexCurrencyPair.of("BTC","USD")); final BitfinexTickerSymbol symbol2 = BitfinexSymbols.ticker(BitfinexCurrencyPair.of("ETH","USD")); final BitfinexTickerSymbol symbol3 = BitfinexSymbols.ticker(BitfinexCurrencyPair.of("EOS","USD")); final BitfinexTickerSymbol symbol4 = BitfinexSymbols.ticker(BitfinexCurrencyPair.of("IOS","USD")); final BitfinexTickerSymbol symbol5 = BitfinexSymbols.ticker(BitfinexCurrencyPair.of("NEO","USD")); final QuoteManager orderbookManager = bitfinexClient.getQuoteManager(); final FutureOperation operation1 = orderbookManager.subscribeTicker(symbol1); final FutureOperation operation2 = orderbookManager.subscribeTicker(symbol2); final FutureOperation operation3 = orderbookManager.subscribeTicker(symbol3); final FutureOperation operation4 = orderbookManager.subscribeTicker(symbol4); final FutureOperation operation5 = orderbookManager.subscribeTicker(symbol5); final List<FutureOperation> operations = Arrays.asList(operation1, operation2, operation3, operation4, operation5); for(final FutureOperation operation : operations) { operation.waitForCompletion(100, TimeUnit.SECONDS); } cfManager.disableConnectionFeature(BitfinexConnectionFeature.SEQ_ALL); Assert.assertFalse(cfManager.isConnectionFeatureEnabled(BitfinexConnectionFeature.SEQ_ALL)); Thread.sleep(2000); Assert.assertEquals(0, cfManager.getActiveConnectionFeatures()); Assert.assertFalse(cfManager.isConnectionFeatureActive(BitfinexConnectionFeature.SEQ_ALL)); Assert.assertEquals(-1, sequenceNumberAuditor.getPrivateSequence()); Assert.assertTrue(sequenceNumberAuditor.getPublicSequence() > 1); Assert.assertFalse(sequenceNumberAuditor.isFailed()); bitfinexClient.close(); } /** * Test the error callback */ @Test(timeout=30000) public void testErrorCallback() { final BitfinexWebsocketClient bitfinexClient = new SimpleBitfinexApiBroker(new BitfinexWebsocketConfiguration(), new BitfinexApiCallbackRegistry(), new SequenceNumberAuditor(), false); // Await at least 5 callbacks final CountDownLatch latch = new CountDownLatch(5); try { bitfinexClient.connect(); final BitfinexCandlestickSymbol symbol = BitfinexSymbols.candlesticks( BitfinexCurrencyPair.of("BTC","USD"), BitfinexCandleTimeFrame.MINUTES_1); final QuoteManager orderbookManager = bitfinexClient.getQuoteManager(); final BiConsumer<BitfinexCandlestickSymbol, BitfinexCandle> callback = (c, o) -> { latch.countDown(); }; // 1st subscribe call orderbookManager.registerCandlestickCallback(symbol, callback); orderbookManager.subscribeCandles(symbol); // 2nd subscribe call orderbookManager.subscribeCandles(symbol); latch.await(); orderbookManager.unsubscribeCandles(symbol); Assert.assertTrue(orderbookManager.removeCandlestickCallback(symbol, callback)); Assert.assertFalse(orderbookManager.removeCandlestickCallback(symbol, callback)); } catch (Exception e) { // Should not happen e.printStackTrace(); Assert.fail(); } finally { bitfinexClient.close(); } } }