Search in sources :

Example 1 with Strategy

use of tech.cassandre.trading.bot.domain.Strategy in project cassandre-trading-bot by cassandre-tech.

the class MultipleStrategiesTest method checkMultipleStrategyBehavior.

@Test
@DisplayName("Check multiple strategies behavior with dry mode")
public void checkMultipleStrategyBehavior() {
    // ==============================================================================================================
    // Strategies tested.
    // Strategy 1 - Requesting BTC/USDT.
    // Strategy 2 - Requesting BTC/ETH.
    // Strategy 3 - Requesting BTC/USDT & ETH/USDT.
    // ==============================================================================================================
    // Checking the MarketService have all the currency pairs.
    final Set<CurrencyPairDTO> availableCurrencyPairs = exchangeService.getAvailableCurrencyPairs();
    assertEquals(3, availableCurrencyPairs.size());
    assertTrue(availableCurrencyPairs.contains(BTC_USDT));
    assertTrue(availableCurrencyPairs.contains(BTC_ETH));
    assertTrue(availableCurrencyPairs.contains(ETH_USDT));
    // ==============================================================================================================
    // Checking the three strategies is stored in the database.
    assertEquals(3, strategyRepository.count());
    final Optional<Strategy> s1 = strategyRepository.findByStrategyId("01");
    assertTrue(s1.isPresent());
    assertEquals(1, s1.get().getUid());
    assertEquals("01", s1.get().getStrategyId());
    assertEquals("Strategy 1", s1.get().getName());
    final Optional<Strategy> s2 = strategyRepository.findByStrategyId("02");
    assertTrue(s2.isPresent());
    assertEquals(2, s2.get().getUid());
    assertEquals("02", s2.get().getStrategyId());
    assertEquals("Strategy 2", s2.get().getName());
    final Optional<Strategy> s3 = strategyRepository.findByStrategyId("03");
    assertTrue(s3.isPresent());
    assertEquals(3, s3.get().getUid());
    assertEquals("03", s3.get().getStrategyId());
    assertEquals("Strategy 3", s3.get().getName());
    // ==============================================================================================================
    // Check balances on each strategy & onAccountUpdate(), we should have the same values in all strategies.
    // As we are in dry mode, balances are loaded from:
    // spring-boot-starter/autoconfigure/src/test/resources/user-main.csv
    // spring-boot-starter/autoconfigure/src/test/resources/user-trade.csv
    // spring-boot-starter/autoconfigure/src/test/resources/user-savings.csv
    accountFlux.update();
    await().untilAsserted(() -> assertEquals(3, strategy3.getAccountsUpdatesReceived().size()));
    // Strategy 1 accounts data test.
    Map<String, AccountDTO> strategy1Accounts = strategy1.getAccounts();
    Optional<AccountDTO> strategy1TradeAccount = strategy1.getTradeAccount();
    assertEquals(3, strategy1Accounts.size());
    assertTrue(strategy1Accounts.containsKey("main"));
    assertTrue(strategy1Accounts.containsKey("trade"));
    assertTrue(strategy1Accounts.containsKey("savings"));
    assertTrue(strategy1TradeAccount.isPresent());
    assertEquals("trade", strategy1TradeAccount.get().getName());
    assertEquals(3, strategy1TradeAccount.get().getBalances().size());
    assertEquals(3, strategy1TradeAccount.get().getBalances().size());
    assertTrue(strategy1TradeAccount.get().getBalance(BTC).isPresent());
    assertEquals(0, new BigDecimal("0.99962937").compareTo(strategy1TradeAccount.get().getBalance(BTC).get().getAvailable()));
    assertTrue(strategy1TradeAccount.get().getBalance(USDT).isPresent());
    assertEquals(0, new BigDecimal("1000").compareTo(strategy1TradeAccount.get().getBalance(USDT).get().getAvailable()));
    assertTrue(strategy1TradeAccount.get().getBalance(ETH).isPresent());
    assertEquals(0, new BigDecimal("10").compareTo(strategy1TradeAccount.get().getBalance(ETH).get().getAvailable()));
    // Strategy 2 accounts data test.
    Map<String, AccountDTO> strategy2Accounts = strategy2.getAccounts();
    Optional<AccountDTO> strategy2TradeAccount = strategy2.getTradeAccount();
    assertEquals(3, strategy2Accounts.size());
    assertTrue(strategy2Accounts.containsKey("main"));
    assertTrue(strategy2Accounts.containsKey("trade"));
    assertTrue(strategy2Accounts.containsKey("savings"));
    assertTrue(strategy2TradeAccount.isPresent());
    assertEquals("trade", strategy2TradeAccount.get().getName());
    assertEquals(3, strategy2TradeAccount.get().getBalances().size());
    assertTrue(strategy2TradeAccount.get().getBalance(BTC).isPresent());
    assertEquals(0, new BigDecimal("0.99962937").compareTo(strategy2TradeAccount.get().getBalance(BTC).get().getAvailable()));
    assertTrue(strategy2TradeAccount.get().getBalance(USDT).isPresent());
    assertEquals(0, new BigDecimal("1000").compareTo(strategy2TradeAccount.get().getBalance(USDT).get().getAvailable()));
    assertTrue(strategy2TradeAccount.get().getBalance(ETH).isPresent());
    assertEquals(0, new BigDecimal("10").compareTo(strategy2TradeAccount.get().getBalance(ETH).get().getAvailable()));
    // Strategy 3 accounts data test.
    Map<String, AccountDTO> strategy3Accounts = strategy3.getAccounts();
    Optional<AccountDTO> strategy3TradeAccount = strategy3.getTradeAccount();
    assertEquals(3, strategy3Accounts.size());
    assertTrue(strategy3Accounts.containsKey("main"));
    assertTrue(strategy3Accounts.containsKey("trade"));
    assertTrue(strategy3Accounts.containsKey("savings"));
    assertTrue(strategy3TradeAccount.isPresent());
    assertEquals("trade", strategy3TradeAccount.get().getName());
    assertEquals(3, strategy3TradeAccount.get().getBalances().size());
    assertTrue(strategy3TradeAccount.get().getBalance(BTC).isPresent());
    assertEquals(0, new BigDecimal("0.99962937").compareTo(strategy3TradeAccount.get().getBalance(BTC).get().getAvailable()));
    assertTrue(strategy3TradeAccount.get().getBalance(USDT).isPresent());
    assertEquals(0, new BigDecimal("1000").compareTo(strategy3TradeAccount.get().getBalance(USDT).get().getAvailable()));
    assertTrue(strategy3TradeAccount.get().getBalance(ETH).isPresent());
    assertEquals(0, new BigDecimal("10").compareTo(strategy3TradeAccount.get().getBalance(ETH).get().getAvailable()));
    // ==============================================================================================================
    // Emitting tickers and checking received tickers by strategies.
    // Sending BTC/USDT - BTC/ETH - ETH/USDT.
    // Strategy 1 - Requesting BTC/USDT should receive one ticker - 1 BTC costs 50 000 USDT.
    // Strategy 2 - Requesting BTC/ETH should receive one ticker - 1 BTC costs 25 ETH.
    // Strategy 3 - Requesting BTC/USDT & ETH/USDT should receive two tickers - 1 ETH costs 2000 USDT.
    tickerFlux.emitValue(TickerDTO.builder().currencyPair(BTC_USDT).last(new BigDecimal("50000")).build());
    tickerFlux.emitValue(TickerDTO.builder().currencyPair(BTC_ETH).last(new BigDecimal("25")).build());
    tickerFlux.emitValue(TickerDTO.builder().currencyPair(ETH_USDT).last(new BigDecimal("2000")).build());
    await().untilAsserted(() -> assertEquals(2, strategy3.getTickersUpdatesReceived().size()));
    // Strategy 1.
    assertEquals(1, strategy1.getTickersUpdatesReceived().size());
    final TickerDTO strategy1Ticker1 = strategy1.getTickersUpdatesReceived().get(0);
    assertNotNull(strategy1Ticker1);
    assertEquals(BTC_USDT, strategy1Ticker1.getCurrencyPair());
    assertEquals(0, new BigDecimal("50000").compareTo(strategy1Ticker1.getLast()));
    // Strategy 2.
    assertEquals(1, strategy2.getTickersUpdatesReceived().size());
    final TickerDTO strategy2Ticker1 = strategy2.getTickersUpdatesReceived().get(0);
    assertNotNull(strategy2Ticker1);
    assertEquals(BTC_ETH, strategy2Ticker1.getCurrencyPair());
    assertEquals(0, new BigDecimal("25").compareTo(strategy2Ticker1.getLast()));
    // Strategy 3.
    assertEquals(2, strategy3.getTickersUpdatesReceived().size());
    final TickerDTO strategy3Ticker1 = strategy3.getTickersUpdatesReceived().get(0);
    assertNotNull(strategy3Ticker1);
    assertEquals(BTC_USDT, strategy3Ticker1.getCurrencyPair());
    assertEquals(0, new BigDecimal("50000").compareTo(strategy3Ticker1.getLast()));
    final TickerDTO strategy3Ticker2 = strategy3.getTickersUpdatesReceived().get(1);
    assertNotNull(strategy3Ticker2);
    assertEquals(ETH_USDT, strategy3Ticker2.getCurrencyPair());
    assertEquals(0, new BigDecimal("2000").compareTo(strategy3Ticker2.getLast()));
    // ==============================================================================================================
    // Strategy 1 - Creating 1 position on BTC/USDT (0.001 BTC for 50 USDT).
    // The price of 1 BTC is 50 000 USDT, and we buy 0.001 BTC for 50 USDT.
    // We stop at 100% gain.
    final PositionCreationResultDTO position1Result = strategy1.createLongPosition(BTC_USDT, new BigDecimal("0.001"), PositionRulesDTO.builder().stopGainPercentage(100f).build());
    assertTrue(position1Result.isSuccessful());
    final long position1Uid = position1Result.getPosition().getUid();
    final long position1PositionId = position1Result.getPosition().getPositionId();
    await().untilAsserted(() -> {
        orderFlux.update();
        tradeFlux.update();
        assertEquals(OPENED, getPositionDTO(position1Uid).getStatus());
    });
    // Check position uid & position id.
    assertEquals(1, position1Uid);
    assertEquals(1, position1PositionId);
    // Check onPositionUpdate() & onPositionStatusUpdate().
    // For strategy 1:
    // 4 positions updates:
    // - Position OPENING with opening order with PENDING_NEW status.
    // - Position OPENING with opening order with NEW status.
    // - Position OPENING with opening order with FILLED status.
    // - Position OPENED with opening order with FILLED status and the corresponding trade.
    // Positions status updates 2: OPENING and then OPENED.
    await().untilAsserted(() -> assertEquals(4, strategy1.getPositionsUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(2, strategy1.getPositionsStatusUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(0, strategy2.getPositionsUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(0, strategy2.getPositionsStatusUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(0, strategy3.getPositionsStatusUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(0, strategy3.getPositionsUpdatesReceived().size()));
    // Check onOrderUpdate().
    // - Order with NEW status.
    // - Order with FILLED status.
    // (PENDING_NEW orders are not in events).
    await().untilAsserted(() -> assertEquals(2, strategy1.getOrdersUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(0, strategy2.getOrdersUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(0, strategy3.getOrdersUpdatesReceived().size()));
    // Check onTradeUpdate().
    // - Trade arrives for the opening order.
    await().untilAsserted(() -> assertEquals(1, strategy1.getTradesUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(0, strategy2.getTradesUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(0, strategy3.getTradesUpdatesReceived().size()));
    // Check getOrders() & getOrderByOrderId().
    // Only strategy 1 should have received an order.
    assertEquals(1, strategy1.getOrders().size());
    assertTrue(strategy1.getOrderByOrderId("DRY_ORDER_000000001").isPresent());
    assertEquals(0, strategy2.getOrders().size());
    assertTrue(strategy2.getOrderByOrderId("DRY_ORDER_000000001").isEmpty());
    assertEquals(0, strategy3.getOrders().size());
    assertTrue(strategy3.getOrderByOrderId("DRY_ORDER_000000001").isEmpty());
    // Check getTrades() & getTradeByTradeId().
    // Only strategy 1 should have received a trade.
    assertEquals(1, strategy1.getTrades().size());
    assertTrue(strategy1.getTradeByTradeId("DRY_TRADE_000000001").isPresent());
    assertEquals(0, strategy2.getTrades().size());
    assertTrue(strategy2.getTradeByTradeId("DRY_TRADE_000000001").isEmpty());
    assertEquals(0, strategy3.getTrades().size());
    assertTrue(strategy3.getTradeByTradeId("DRY_TRADE_000000001").isEmpty());
    // Check getAmountsLockedByPosition().
    BigDecimal amountLockedForBTC = strategy1.getAmountsLockedByCurrency(BTC);
    assertEquals(0, new BigDecimal("0.001").compareTo(amountLockedForBTC));
    amountLockedForBTC = strategy2.getAmountsLockedByCurrency(BTC);
    assertEquals(0, new BigDecimal("0.001").compareTo(amountLockedForBTC));
    amountLockedForBTC = strategy3.getAmountsLockedByCurrency(BTC);
    assertEquals(0, new BigDecimal("0.001").compareTo(amountLockedForBTC));
    // ==============================================================================================================
    // Strategy 2 - Creating 1 position on BTC/ETH (0.2 BTC for 0.5 ETH).
    // The price of 1 BTC is 25 ETH, and we buy 0.02 BTC for 0.5 ETH.
    // We stop at 100% gain.
    final PositionCreationResultDTO position2Result = strategy2.createLongPosition(BTC_ETH, new BigDecimal("0.02"), PositionRulesDTO.builder().stopGainPercentage(100f).build());
    assertTrue(position2Result.isSuccessful());
    final long position2Uid = position2Result.getPosition().getUid();
    final long position2PositionId = position2Result.getPosition().getPositionId();
    await().untilAsserted(() -> {
        orderFlux.update();
        tradeFlux.update();
        assertEquals(OPENED, getPositionDTO(position2Uid).getStatus());
    });
    // Check position uid & position id.
    assertEquals(2, position2Uid);
    assertEquals(1, position2PositionId);
    // Check onPositionUpdate() & onPositionStatusUpdate().
    // For strategy 2:
    // 4 positions updates:
    // - Position OPENING with opening order with PENDING_NEW status.
    // - Position OPENING with opening order with NEW status.
    // - Position OPENING with opening order with FILLED status.
    // - Position OPENED with opening order with FILLED status and the corresponding trade.
    // Positions status updates 2: OPENING and then OPENED.
    await().untilAsserted(() -> assertEquals(4, strategy1.getPositionsUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(2, strategy1.getPositionsStatusUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(4, strategy2.getPositionsUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(2, strategy2.getPositionsStatusUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(0, strategy3.getPositionsStatusUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(0, strategy3.getPositionsUpdatesReceived().size()));
    // Check onOrderUpdate().
    // - Order with NEW status.
    // - Order with FILLED status.
    // (PENDING_NEW orders are not in events).
    await().untilAsserted(() -> assertEquals(2, strategy1.getOrdersUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(2, strategy2.getOrdersUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(0, strategy3.getOrdersUpdatesReceived().size()));
    // Check onTradeUpdate().
    // - Trade arrives for the opening order.
    await().untilAsserted(() -> assertEquals(1, strategy1.getTradesUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(1, strategy2.getTradesUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(0, strategy3.getTradesUpdatesReceived().size()));
    // Check getOrders() & getOrderByOrderId().
    assertEquals(1, strategy1.getOrders().size());
    assertTrue(strategy1.getOrderByOrderId("DRY_ORDER_000000001").isPresent());
    assertEquals(1, strategy2.getOrders().size());
    assertTrue(strategy2.getOrderByOrderId("DRY_ORDER_000000002").isPresent());
    assertEquals(0, strategy3.getOrders().size());
    assertTrue(strategy3.getOrderByOrderId("DRY_ORDER_000000001").isEmpty());
    // Check getTrades() & getTradeByTradeId().
    assertEquals(1, strategy1.getTrades().size());
    assertTrue(strategy1.getTradeByTradeId("DRY_TRADE_000000001").isPresent());
    assertEquals(1, strategy2.getTrades().size());
    assertTrue(strategy2.getTradeByTradeId("DRY_TRADE_000000002").isPresent());
    assertEquals(0, strategy3.getTrades().size());
    assertTrue(strategy3.getTradeByTradeId("DRY_TRADE_000000001").isEmpty());
    // Check getAmountsLockedByPosition().
    amountLockedForBTC = strategy1.getAmountsLockedByCurrency(BTC);
    assertEquals(0, new BigDecimal("0.021").compareTo(amountLockedForBTC));
    amountLockedForBTC = strategy2.getAmountsLockedByCurrency(BTC);
    assertEquals(0, new BigDecimal("0.021").compareTo(amountLockedForBTC));
    amountLockedForBTC = strategy3.getAmountsLockedByCurrency(BTC);
    assertEquals(0, new BigDecimal("0.021").compareTo(amountLockedForBTC));
    // =============================================================================================================
    // Changing the price for BTC/USDT.
    // A bitcoin now costs 10000 USDT and the price of position should have change.
    tickerFlux.emitValue(TickerDTO.builder().currencyPair(BTC_USDT).last(new BigDecimal("10000")).build());
    await().untilAsserted(() -> assertEquals(3, strategy3.getTickersUpdatesReceived().size()));
    assertEquals(2, strategy1.getTickersUpdatesReceived().size());
    assertEquals(1, strategy2.getTickersUpdatesReceived().size());
    assertEquals(3, strategy3.getTickersUpdatesReceived().size());
    // =============================================================================================================
    // Strategy 3
    // - Creating one position on BTC/USDT (0.01 BTC for 100 USDT).
    final PositionCreationResultDTO position3Result = strategy3.createLongPosition(BTC_USDT, new BigDecimal("0.01"), PositionRulesDTO.builder().stopGainPercentage(100f).build());
    assertTrue(position3Result.isSuccessful());
    final long position3Uid = position3Result.getPosition().getUid();
    final long position3PositionId = position3Result.getPosition().getPositionId();
    await().untilAsserted(() -> {
        orderFlux.update();
        tradeFlux.update();
        assertEquals(OPENED, getPositionDTO(position3Uid).getStatus());
    });
    // - Creating one position on ETH/USDT (0.1 ETH for 200 USDT).
    final PositionCreationResultDTO position4Result = strategy3.createLongPosition(ETH_USDT, new BigDecimal("0.1"), PositionRulesDTO.builder().stopGainPercentage(100f).build());
    assertTrue(position4Result.isSuccessful());
    final long position4Uid = position4Result.getPosition().getUid();
    final long position4PositionId = position4Result.getPosition().getPositionId();
    await().untilAsserted(() -> {
        orderFlux.update();
        tradeFlux.update();
        assertEquals(OPENED, getPositionDTO(position4Uid).getStatus());
    });
    // Check position uid & position id.
    assertEquals(3, position3Uid);
    assertEquals(1, position3PositionId);
    assertEquals(4, position4Uid);
    assertEquals(2, position4PositionId);
    // Check onPositionUpdate() & onPositionStatusUpdate().
    // 8 positions updates:
    // - 2 positions OPENING with opening order with PENDING_NEW status.
    // - 2 positions OPENING with opening order with NEW status.
    // - 2 positions OPENING with opening order with FILLED status.
    // - 2 positions OPENED with opening order with FILLED status and the corresponding trade.
    // Positions status updates 4: 2 orders OPENING and then OPENED.
    await().untilAsserted(() -> assertEquals(5, strategy1.getPositionsUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(2, strategy1.getPositionsStatusUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(4, strategy2.getPositionsUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(2, strategy2.getPositionsStatusUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(8, strategy3.getPositionsUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(4, strategy3.getPositionsStatusUpdatesReceived().size()));
    // Check onOrderUpdate().
    await().untilAsserted(() -> assertEquals(2, strategy1.getOrdersUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(2, strategy2.getOrdersUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(4, strategy3.getOrdersUpdatesReceived().size()));
    // Check onTradeUpdate().
    await().untilAsserted(() -> assertEquals(1, strategy1.getTradesUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(1, strategy2.getTradesUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(2, strategy3.getTradesUpdatesReceived().size()));
    // Check getOrders() & getOrderByOrderId().
    // - 2 orders with NEW status.
    // - 2 orders with FILLED status.
    // (PENDING_NEW orders are not in events).
    assertEquals(1, strategy1.getOrders().size());
    assertTrue(strategy1.getOrderByOrderId("DRY_ORDER_000000001").isPresent());
    assertEquals(1, strategy2.getOrders().size());
    assertTrue(strategy2.getOrderByOrderId("DRY_ORDER_000000002").isPresent());
    assertEquals(2, strategy3.getOrders().size());
    assertTrue(strategy3.getOrderByOrderId("DRY_ORDER_000000003").isPresent());
    assertTrue(strategy3.getOrderByOrderId("DRY_ORDER_000000004").isPresent());
    // Check getTrades() & getTradeByTradeId().
    // - 2 trades arrives for the 2 opening orders.
    assertEquals(1, strategy1.getTrades().size());
    assertTrue(strategy1.getTradeByTradeId("DRY_TRADE_000000001").isPresent());
    assertEquals(1, strategy2.getTrades().size());
    assertTrue(strategy2.getTradeByTradeId("DRY_TRADE_000000002").isPresent());
    assertEquals(2, strategy3.getTrades().size());
    assertTrue(strategy3.getTradeByTradeId("DRY_TRADE_000000003").isPresent());
    assertTrue(strategy3.getTradeByTradeId("DRY_TRADE_000000004").isPresent());
    // Check getAmountsLockedByPosition().
    amountLockedForBTC = strategy1.getAmountsLockedByCurrency(BTC);
    assertEquals(0, new BigDecimal("0.031").compareTo(amountLockedForBTC));
    amountLockedForBTC = strategy2.getAmountsLockedByCurrency(BTC);
    assertEquals(0, new BigDecimal("0.031").compareTo(amountLockedForBTC));
    amountLockedForBTC = strategy3.getAmountsLockedByCurrency(BTC);
    assertEquals(0, new BigDecimal("0.031").compareTo(amountLockedForBTC));
    BigDecimal amountLockedForETH = strategy1.getAmountsLockedByCurrency(ETH);
    assertEquals(0, new BigDecimal("0.1").compareTo(amountLockedForETH));
    amountLockedForETH = strategy2.getAmountsLockedByCurrency(ETH);
    assertEquals(0, new BigDecimal("0.1").compareTo(amountLockedForETH));
    amountLockedForETH = strategy3.getAmountsLockedByCurrency(ETH);
    assertEquals(0, new BigDecimal("0.1").compareTo(amountLockedForETH));
    // ==============================================================================================================
    // Check balances, canBuy() & canSell().
    // At the beginning my balances was:
    // - 0.99962937 BTC.
    // - 1 000 USDT.
    // - 10 ETH.
    // ---
    // Position 1 - Bought 0.001 BTC for 50 USDT - BTC/USDT : 50000.
    // Position 2 - Bought 0.2 BTC for 0.5 ETH - BTC/ETH : 25.
    // Position 3 - Bought 0.01 BTC for 100 USDT - BTC/USDT : 10000
    // Position 4 - Bought 0.1 ETH for 200 USDT - ETH/USDT : 2000.
    // --
    // BTC : 0.99962937 + 0.001 (P1) + 0.02 (P2) + 0.01 (P2) = 1.03062937 BTC
    // USDT : 1000 - 50 (P1) - 100 (P3) - 200 (P4) = 650 USDT
    // ETH : 10 - 0.5 (P2) + 0.1 (P4) = 5.1 ETH
    accountFlux.update();
    await().untilAsserted(() -> assertEquals(4, strategy3.getAccountsUpdatesReceived().size()));
    // Strategy 1 balances.
    assertEquals(0, new BigDecimal("1.03062937").compareTo(strategy1.getTradeAccountBalances().get(BTC).getAvailable()));
    assertEquals(0, new BigDecimal("650").compareTo(strategy1.getTradeAccountBalances().get(USDT).getAvailable()));
    assertEquals(0, new BigDecimal("9.6").compareTo(strategy1.getTradeAccountBalances().get(ETH).getAvailable()));
    // Strategy 2 balances.
    assertEquals(0, new BigDecimal("1.03062937").compareTo(strategy2.getTradeAccountBalances().get(BTC).getAvailable()));
    assertEquals(0, new BigDecimal("650").compareTo(strategy2.getTradeAccountBalances().get(USDT).getAvailable()));
    assertEquals(0, new BigDecimal("9.6").compareTo(strategy2.getTradeAccountBalances().get(ETH).getAvailable()));
    // Strategy 3 balances.
    assertEquals(0, new BigDecimal("1.03062937").compareTo(strategy3.getTradeAccountBalances().get(BTC).getAvailable()));
    assertEquals(0, new BigDecimal("650").compareTo(strategy3.getTradeAccountBalances().get(USDT).getAvailable()));
    assertEquals(0, new BigDecimal("9.6").compareTo(strategy3.getTradeAccountBalances().get(ETH).getAvailable()));
    // ==============================================================================================================
    // New ticker on BTC USDT to close a position.
    // Before : BTC/USDT - 10 000
    // New : BTC/USDT - 20 000 => Should trigger closing Position 3 (Position 1 of strategy 3).
    // Position 1 - Bought 0.001 BTC for 50 USDT - BTC/USDT : 50000.
    // Position 2 - Bought 0.2 BTC for 0.5 ETH - BTC/ETH : 25.
    // Position 3 - Bought 0.01 BTC for 100 USDT - BTC/USDT : 20000 => Now close, sold 0.01 BTC for 200 USDT.
    // Position 4 - Bought 0.1 ETH for 200 USDT - ETH/USDT : 2000.
    tickerFlux.emitValue(TickerDTO.builder().currencyPair(BTC_USDT).last(new BigDecimal("20000")).build());
    await().untilAsserted(() -> assertEquals(CLOSING, getPositionDTO(position3Uid).getStatus()));
    await().untilAsserted(() -> {
        orderFlux.update();
        tradeFlux.update();
        assertEquals(CLOSED, getPositionDTO(position3Uid).getStatus());
    });
    // Check position status.
    assertEquals(OPENED, getPositionDTO(position1Uid).getStatus());
    assertEquals(OPENED, getPositionDTO(position2Uid).getStatus());
    assertEquals(CLOSED, getPositionDTO(position3Uid).getStatus());
    assertEquals(OPENED, getPositionDTO(position4Uid).getStatus());
    // Check onPositionUpdate() & onPositionStatusUpdate().
    // strategy1: 1 position update because of prices updates.
    // strategy3, 5 position updates:
    // - Position prices updates.
    // - Position CLOSING with opening order with PENDING_NEW status.
    // - Position CLOSING with opening order with NEW status.
    // - Position CLOSING with opening order with FILLED status.
    // - Position CLOSED with opening order with FILLED status and the corresponding trade.
    // Positions status updates : CLOSING and then CLOSED.
    await().untilAsserted(() -> assertEquals(6, strategy1.getPositionsUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(2, strategy1.getPositionsStatusUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(4, strategy2.getPositionsUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(2, strategy2.getPositionsStatusUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(13, strategy3.getPositionsUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(6, strategy3.getPositionsStatusUpdatesReceived().size()));
    // Check onOrderUpdate().
    // - Order with NEW status.
    // - Order with FILLED status.
    // (PENDING_NEW orders are not in events).
    await().untilAsserted(() -> assertEquals(2, strategy1.getOrdersUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(2, strategy2.getOrdersUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(6, strategy3.getOrdersUpdatesReceived().size()));
    // Check onTradeUpdate().
    // - Trade arrives for the closing order.
    await().untilAsserted(() -> assertEquals(1, strategy1.getTradesUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(1, strategy2.getTradesUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(3, strategy3.getTradesUpdatesReceived().size()));
    // Check getOrders() & getOrderByOrderId().
    assertEquals(1, strategy1.getOrders().size());
    assertTrue(strategy1.getOrderByOrderId("DRY_ORDER_000000001").isPresent());
    assertEquals(1, strategy2.getOrders().size());
    assertTrue(strategy2.getOrderByOrderId("DRY_ORDER_000000002").isPresent());
    assertEquals(3, strategy3.getOrders().size());
    assertTrue(strategy3.getOrderByOrderId("DRY_ORDER_000000003").isPresent());
    assertTrue(strategy3.getOrderByOrderId("DRY_ORDER_000000004").isPresent());
    assertTrue(strategy3.getOrderByOrderId("DRY_ORDER_000000005").isPresent());
    // Check getTrades() & getTradeByTradeId().
    assertEquals(1, strategy1.getTrades().size());
    assertTrue(strategy1.getTradeByTradeId("DRY_TRADE_000000001").isPresent());
    assertEquals(1, strategy2.getTrades().size());
    assertTrue(strategy2.getTradeByTradeId("DRY_TRADE_000000002").isPresent());
    assertEquals(3, strategy3.getTrades().size());
    assertTrue(strategy3.getTradeByTradeId("DRY_TRADE_000000003").isPresent());
    assertTrue(strategy3.getTradeByTradeId("DRY_TRADE_000000004").isPresent());
    assertTrue(strategy3.getTradeByTradeId("DRY_TRADE_000000005").isPresent());
    // Check getAmountsLockedByPosition().
    // Position 3 - Bought 0.01 BTC for 100 USDT - BTC/USDT : 20000 => Now close, sold 0.01 BTC for 200 USDT.
    amountLockedForBTC = strategy1.getAmountsLockedByCurrency(BTC);
    assertEquals(0, new BigDecimal("0.021").compareTo(amountLockedForBTC));
    amountLockedForBTC = strategy2.getAmountsLockedByCurrency(BTC);
    assertEquals(0, new BigDecimal("0.021").compareTo(amountLockedForBTC));
    amountLockedForBTC = strategy3.getAmountsLockedByCurrency(BTC);
    assertEquals(0, new BigDecimal("0.021").compareTo(amountLockedForBTC));
    amountLockedForETH = strategy1.getAmountsLockedByCurrency(ETH);
    assertEquals(0, new BigDecimal("0.1").compareTo(amountLockedForETH));
    amountLockedForETH = strategy2.getAmountsLockedByCurrency(ETH);
    assertEquals(0, new BigDecimal("0.1").compareTo(amountLockedForETH));
    amountLockedForETH = strategy3.getAmountsLockedByCurrency(ETH);
    assertEquals(0, new BigDecimal("0.1").compareTo(amountLockedForETH));
    // ==============================================================================================================
    // Check balances, canBuy() & canSell().
    // At the beginning my balances was:
    // - 0.99962937 BTC.
    // - 1 000 USDT.
    // - 10 ETH.
    // ---
    // Position 1 - Bought 0.001 BTC for 50 USDT - BTC/USDT : 50000.
    // Position 2 - Bought 0.2 BTC for 0.5 ETH - BTC/ETH : 25.
    // Position 3 - Bought 0.01 BTC for 100 USDT - BTC/USDT : 10000
    // Position 4 - Bought 0.1 ETH for 200 USDT - ETH/USDT : 2000.
    // New event.
    // Position 3 - Bought 0.01 BTC for 100 USDT - BTC/USDT : 20000 => Now close, sold 0.01 BTC for 200 USDT.
    // --
    // BTC : 0.99962937 + 0.001 (P1) + 0.02 (P2) + 0.01 (P2) - 0.01 (P3 close) = 1.02062937 BTC
    // USDT : 1000 - 50 (P1) - 100 (P3) - 200 (P4) + 200 (P3) = 850 USDT
    // ETH : 10 - 0.5 (P2) + 0.1 (P4) = 5.1 ETH
    accountFlux.update();
    await().untilAsserted(() -> assertEquals(5, strategy3.getAccountsUpdatesReceived().size()));
    // Strategy 1.
    assertEquals(0, new BigDecimal("1.02062937").compareTo(strategy1.getTradeAccountBalances().get(BTC).getAvailable()));
    assertEquals(0, new BigDecimal("850").compareTo(strategy1.getTradeAccountBalances().get(USDT).getAvailable()));
    assertEquals(0, new BigDecimal("9.6").compareTo(strategy1.getTradeAccountBalances().get(ETH).getAvailable()));
    // Strategy 2.
    assertEquals(0, new BigDecimal("1.02062937").compareTo(strategy2.getTradeAccountBalances().get(BTC).getAvailable()));
    assertEquals(0, new BigDecimal("850").compareTo(strategy2.getTradeAccountBalances().get(USDT).getAvailable()));
    assertEquals(0, new BigDecimal("9.6").compareTo(strategy2.getTradeAccountBalances().get(ETH).getAvailable()));
    // Strategy 3.
    assertEquals(0, new BigDecimal("1.02062937").compareTo(strategy3.getTradeAccountBalances().get(BTC).getAvailable()));
    assertEquals(0, new BigDecimal("850").compareTo(strategy3.getTradeAccountBalances().get(USDT).getAvailable()));
    assertEquals(0, new BigDecimal("9.6").compareTo(strategy3.getTradeAccountBalances().get(ETH).getAvailable()));
}
Also used : PositionCreationResultDTO(tech.cassandre.trading.bot.dto.position.PositionCreationResultDTO) CurrencyPairDTO(tech.cassandre.trading.bot.dto.util.CurrencyPairDTO) TickerDTO(tech.cassandre.trading.bot.dto.market.TickerDTO) Strategy(tech.cassandre.trading.bot.domain.Strategy) AccountDTO(tech.cassandre.trading.bot.dto.user.AccountDTO) BigDecimal(java.math.BigDecimal) BaseTest(tech.cassandre.trading.bot.test.util.junit.BaseTest) Test(org.junit.jupiter.api.Test) SpringBootTest(org.springframework.boot.test.context.SpringBootTest) DisplayName(org.junit.jupiter.api.DisplayName)

Example 2 with Strategy

use of tech.cassandre.trading.bot.domain.Strategy in project cassandre-trading-bot by cassandre-tech.

the class StrategiesAutoConfiguration method configure.

/**
 * Search for strategies and runs them.
 */
@PostConstruct
@SuppressWarnings("checkstyle:MethodLength")
public void configure() {
    // Retrieving all the beans have the @Strategy annotation.
    final Map<String, Object> strategies = applicationContext.getBeansWithAnnotation(CassandreStrategy.class);
    // =============================================================================================================
    // Configuration check.
    // We run tests to display and check if everything is ok with the configuration.
    final UserDTO user = checkConfiguration(strategies);
    // =============================================================================================================
    // Maintenance code.
    // If a position is blocked in OPENING or CLOSING, we send again the trades.
    // This could happen if cassandre crashes after saving a trade and did not have time to send it to
    // positionService. Here we force the status recalculation in PositionDTO, and we save it.
    positionRepository.findByStatusIn(Stream.of(OPENING, CLOSING).toList()).stream().map(POSITION_MAPPER::mapToPositionDTO).map(POSITION_MAPPER::mapToPosition).forEach(positionRepository::save);
    // =============================================================================================================
    // Importing tickers & candles into database.
    // Feature documentation is here: https://trading-bot.cassandre.tech/learn/import-historical-data.html
    loadTickersFromFiles();
    loadCandlesFromFiles();
    // =============================================================================================================
    // Creating flux.
    final ConnectableFlux<Set<AccountDTO>> connectableAccountFlux = accountFlux.getFlux().publish();
    final ConnectableFlux<Set<PositionDTO>> connectablePositionFlux = positionFlux.getFlux().publish();
    final ConnectableFlux<Set<OrderDTO>> connectableOrderFlux = orderFlux.getFlux().publish();
    final ConnectableFlux<Set<TickerDTO>> connectableTickerFlux = tickerFlux.getFlux().publish();
    final ConnectableFlux<Set<TradeDTO>> connectableTradeFlux = tradeFlux.getFlux().publish();
    // =============================================================================================================
    // Configuring strategies.
    // Data in database, services, flux...
    logger.info("Running the following strategies:");
    strategies.values().forEach(s -> {
        CassandreStrategyInterface strategy = (CassandreStrategyInterface) s;
        CassandreStrategy annotation = s.getClass().getAnnotation(CassandreStrategy.class);
        // Retrieving strategy information from annotation.
        final String strategyId = annotation.strategyId();
        final String strategyName = annotation.strategyName();
        // Displaying information about strategy.
        logger.info("- Strategy '{}/{}' (requires {})", strategyId, strategyName, strategy.getRequestedCurrencyPairs().stream().map(CurrencyPairDTO::toString).collect(Collectors.joining(", ")));
        // StrategyDTO: saving or updating the strategy in database.
        StrategyDTO strategyDTO;
        final Optional<Strategy> strategyInDatabase = strategyRepository.findByStrategyId(annotation.strategyId());
        if (strategyInDatabase.isEmpty()) {
            // =============================================================================================
            // If the strategy is NOT in database.
            Strategy newStrategy = new Strategy();
            newStrategy.setStrategyId(annotation.strategyId());
            newStrategy.setName(annotation.strategyName());
            strategyDTO = STRATEGY_MAPPER.mapToStrategyDTO(strategyRepository.save(newStrategy));
            logger.debug("Strategy created in database: {}", newStrategy);
        } else {
            // =============================================================================================
            // If the strategy is in database.
            strategyInDatabase.get().setName(strategyName);
            strategyDTO = STRATEGY_MAPPER.mapToStrategyDTO(strategyRepository.save(strategyInDatabase.get()));
            logger.debug("Strategy updated in database: {}", strategyInDatabase.get());
        }
        strategyDTO.initializeLastPositionIdUsed(positionRepository.getLastPositionIdUsedByStrategy(strategyDTO.getUid()));
        // Setting up configuration, dependencies and accounts in strategy.
        strategy.initializeAccounts(user.getAccounts());
        strategy.setConfiguration(getCassandreStrategyConfiguration(strategyDTO));
        strategy.setDependencies(getCassandreStrategyDependencies());
        // Calling user defined initialize() method.
        strategy.initialize();
        // Connecting flux to strategy.
        connectableAccountFlux.subscribe(strategy::accountsUpdates, throwable -> logger.error("AccountsUpdates failing: {}", throwable.getMessage()));
        connectablePositionFlux.subscribe(strategy::positionsUpdates, throwable -> logger.error("PositionsUpdates failing: {}", throwable.getMessage()));
        connectableOrderFlux.subscribe(strategy::ordersUpdates, throwable -> logger.error("OrdersUpdates failing: {}", throwable.getMessage()));
        connectableTradeFlux.subscribe(strategy::tradesUpdates, throwable -> logger.error("TradesUpdates failing: {}", throwable.getMessage()));
        connectableTickerFlux.subscribe(strategy::tickersUpdates, throwable -> logger.error("TickersUpdates failing: {}", throwable.getMessage()));
    });
    // =============================================================================================================
    // Starting flux.
    connectableAccountFlux.connect();
    connectablePositionFlux.connect();
    connectableOrderFlux.connect();
    connectableTradeFlux.connect();
    connectableTickerFlux.connect();
}
Also used : Set(java.util.Set) HashSet(java.util.HashSet) CassandreStrategy(tech.cassandre.trading.bot.strategy.CassandreStrategy) StrategyDTO(tech.cassandre.trading.bot.dto.strategy.StrategyDTO) UserDTO(tech.cassandre.trading.bot.dto.user.UserDTO) CassandreStrategyInterface(tech.cassandre.trading.bot.strategy.internal.CassandreStrategyInterface) CurrencyPairDTO(tech.cassandre.trading.bot.dto.util.CurrencyPairDTO) CassandreStrategy(tech.cassandre.trading.bot.strategy.CassandreStrategy) Strategy(tech.cassandre.trading.bot.domain.Strategy) PostConstruct(javax.annotation.PostConstruct)

Example 3 with Strategy

use of tech.cassandre.trading.bot.domain.Strategy in project cassandre-trading-bot by cassandre-tech.

the class BasicCassandreStrategyTest method checkStrategyBehavior.

@Test
@DisplayName("Check strategy behavior")
public void checkStrategyBehavior() {
    // =============================================================================================================
    // We check that the strategy is correctly registered in database.
    Optional<Strategy> strategyInDatabase = strategyRepository.findByStrategyId("01");
    assertTrue(strategyInDatabase.isPresent());
    assertEquals(1, strategyInDatabase.get().getUid());
    assertEquals("01", strategyInDatabase.get().getStrategyId());
    assertEquals("Testable strategy", strategyInDatabase.get().getName());
    // =============================================================================================================
    // We check accounts updates (4 replies : account 01, account 02, account 03 and again account 03).
    // But the 4 replies only concerns 3 account.
    // We have 1 BTC, 10 ETH and 100 USDT.
    with().await().untilAsserted(() -> assertEquals(4, strategy.getAccountsUpdatesReceived().size()));
    assertEquals(3, strategy.getAccounts().size());
    // Testing trade account.
    final Optional<AccountDTO> tradeAccount = strategy.getTradeAccount();
    assertTrue(tradeAccount.isPresent());
    assertEquals("03", tradeAccount.get().getAccountId());
    assertEquals("trade", tradeAccount.get().getName());
    // Testing trade account balances.
    assertEquals(3, strategy.getTradeAccountBalances().size());
    assertEquals(0, new BigDecimal("1").compareTo(strategy.getTradeAccountBalances().get(BTC).getAvailable()));
    assertEquals(0, new BigDecimal("10").compareTo(strategy.getTradeAccountBalances().get(ETH).getAvailable()));
    assertEquals(0, new BigDecimal("100").compareTo(strategy.getTradeAccountBalances().get(USDT).getAvailable()));
    // =============================================================================================================
    // We check that all tickers arrived (6 ETH/BTC & 1 ETH/USDT).
    // 1 ETH = 6 BTC.
    // 1 ETH = 10 000 USDT.
    with().await().untilAsserted(() -> assertEquals(7, strategy.getTickersUpdatesReceived().size()));
    assertEquals(2, strategy.getLastTickers().size());
    assertNotNull(strategy.getLastTickers().get(ETH_BTC));
    assertNotNull(strategy.getLastTickers().get(ETH_USDT));
    assertEquals(0, new BigDecimal("6").compareTo(strategy.getLastTickers().get(ETH_BTC).getLast()));
    assertEquals(0, new BigDecimal("10000").compareTo(strategy.getLastTickers().get(ETH_USDT).getLast()));
    // =============================================================================================================
    // Check getEstimatedBuyingCost().
    // As 1 ETH cost 6 BTC, 2 ETH would cost 12 BTC.
    final Optional<CurrencyAmountDTO> estimatedBuyingCost = strategy.getEstimatedBuyingCost(ETH_BTC, new BigDecimal(2));
    assertTrue(estimatedBuyingCost.isPresent());
    assertEquals(0, new BigDecimal("12").compareTo(estimatedBuyingCost.get().getValue()));
    // =============================================================================================================
    // Check getEstimatedBuyableAmount()
    // As 1 ETH cost 10 000 USDT, with 5 000 USDT, I should be able to buy 0.5 ETH.
    // And I check I can't 260 EUR with ETH as I don't have EURO price.
    // And I check I can't buy USDT with EURO as I don't have EURO.
    final Optional<BigDecimal> estimatedBuyableAmount = strategy.getEstimatedBuyableAmount(new CurrencyAmountDTO(new BigDecimal(5000), USDT), ETH);
    assertTrue(estimatedBuyableAmount.isPresent());
    assertEquals(0, new BigDecimal("0.5").compareTo(estimatedBuyableAmount.get()));
    assertFalse(strategy.getEstimatedBuyableAmount(new CurrencyAmountDTO(new BigDecimal(260), EUR), ETH).isPresent());
    assertFalse(strategy.getEstimatedBuyableAmount(new CurrencyAmountDTO(new BigDecimal(260), USDT), EUR).isPresent());
    // =============================================================================================================
    // Check canBuy() & canSell().
    // Our assets: we have 1 BTC, 10 ETH and 100 USDT.
    // 1 ETH costs 6 BTC.
    // 1 ETH costs 10 000 USDT.
    final AccountDTO account = strategy.getAccounts().get("03");
    assertNotNull(account);
    // canBuy().
    // Buying something for an asset we don't have.
    assertFalse(strategy.canBuy(BTC_ETH, new BigDecimal("0.00001")));
    assertFalse(strategy.canBuy(account, BTC_ETH, new BigDecimal("0.00001")));
    // Trying to buy a full bitcoin, but we only have 2 000 USDT.
    assertFalse(strategy.canBuy(ETH_USDT, new BigDecimal("1")));
    assertFalse(strategy.canBuy(account, ETH_USDT, new BigDecimal("1")));
    // Trying to buy a 0.01 ETH that will cost 100 USDT, and we have 100 USDT, it should work but not for 0.011.
    assertTrue(strategy.canBuy(ETH_USDT, new BigDecimal("0.01")));
    assertTrue(strategy.canBuy(account, ETH_USDT, new BigDecimal("0.01")));
    assertFalse(strategy.canBuy(account, ETH_USDT, new BigDecimal("0.011")));
    // Trying to buy a 0.01 bitcoin that costs 100 USDT (we have 100 USDT). But we want to have 1 USDT left.
    assertFalse(strategy.canBuy(ETH_USDT, new BigDecimal("0.01"), new BigDecimal("1")));
    assertFalse(strategy.canBuy(ETH_USDT, new BigDecimal("0.01"), new BigDecimal("1")));
    assertFalse(strategy.canBuy(account, ETH_USDT, new BigDecimal("0.01"), new BigDecimal("1")));
    assertFalse(strategy.canBuy(account, ETH_USDT, new BigDecimal("0.01"), new BigDecimal("1")));
    // canSell().
    // Selling an asset we don't have (EUR).
    assertFalse(strategy.canSell(EUR, new BigDecimal("0.00001")));
    assertFalse(strategy.canSell(account, EUR, new BigDecimal("0.00001")));
    // Trying to sell 1 BTC (we have them).
    assertTrue(strategy.canSell(BTC, new BigDecimal("1")));
    assertTrue(strategy.canSell(account, BTC, new BigDecimal("1")));
    // Trying to sell 3 BTC (we don't have them).
    assertFalse(strategy.canSell(BTC, new BigDecimal("3")));
    assertFalse(strategy.canSell(account, BTC, new BigDecimal("3")));
    // Trying to sell 1 BTC and still have 1 (not possible).
    assertFalse(strategy.canSell(BTC, new BigDecimal("1"), new BigDecimal("1")));
    assertFalse(strategy.canSell(account, BTC, new BigDecimal("1"), new BigDecimal("1")));
}
Also used : CurrencyAmountDTO(tech.cassandre.trading.bot.dto.util.CurrencyAmountDTO) Strategy(tech.cassandre.trading.bot.domain.Strategy) TestableCassandreStrategy(tech.cassandre.trading.bot.test.util.strategies.TestableCassandreStrategy) AccountDTO(tech.cassandre.trading.bot.dto.user.AccountDTO) BigDecimal(java.math.BigDecimal) BaseTest(tech.cassandre.trading.bot.test.util.junit.BaseTest) Test(org.junit.jupiter.api.Test) SpringBootTest(org.springframework.boot.test.context.SpringBootTest) DisplayName(org.junit.jupiter.api.DisplayName)

Example 4 with Strategy

use of tech.cassandre.trading.bot.domain.Strategy in project cassandre-trading-bot by cassandre-tech.

the class BasicTa4jCassandreStrategyTest method checkStrategyBehavior.

@Test
@DisplayName("Check strategy behavior")
public void checkStrategyBehavior() {
    // Check type.
    Optional<Strategy> strategyInDatabase = strategyRepository.findByStrategyId("01");
    assertTrue(strategyInDatabase.isPresent());
    assertEquals(BASIC_TA4J_STRATEGY, strategyInDatabase.get().getType());
    await().untilAsserted(() -> assertEquals(3, strategy.getAccounts().size()));
    await().untilAsserted(() -> assertEquals(4, strategy.getOrders().size()));
    await().untilAsserted(() -> assertEquals(3, strategy.getTrades().size()));
    await().untilAsserted(() -> assertEquals(15, strategy.getTickersUpdateReceived().size()));
    await().untilAsserted(() -> assertEquals(1, strategy.getLastTickers().size()));
    await().untilAsserted(() -> assertEquals(0, new BigDecimal("130").compareTo(strategy.getLastTickers().get(BTC_USDT).getLast())));
    // Check ta4j results.
    // await().untilAsserted(() -> assertEquals(5, strategy.getEnterCount()));
    // await().untilAsserted(() -> assertEquals(2, strategy.getExitCount()));
    // TODO: discuss the above - is it really correct? The bars have same value, there should be no entry
    await().untilAsserted(() -> assertEquals(7, strategy.getSeries().getBarCount()));
    // Check getEstimatedBuyingCost()
    assertTrue(strategy.getEstimatedBuyingCost(BTC_USDT, new BigDecimal(3)).isPresent());
    assertEquals(0, new BigDecimal("390").compareTo(strategy.getEstimatedBuyingCost(BTC_USDT, new BigDecimal(3)).get().getValue()));
    // Check getEstimatedBuyableAmount()
    // 1 BTC = 130 USDT.
    final Optional<BigDecimal> buyableAmount = strategy.getEstimatedBuyableAmount(new CurrencyAmountDTO(new BigDecimal(260), USDT), BTC);
    assertTrue(buyableAmount.isPresent());
    assertEquals(0, new BigDecimal("2").compareTo(buyableAmount.get()));
    assertFalse(strategy.getEstimatedBuyableAmount(new CurrencyAmountDTO(new BigDecimal(260), ETH), BTC).isPresent());
    assertFalse(strategy.getEstimatedBuyableAmount(new CurrencyAmountDTO(new BigDecimal(260), USDT), ETH).isPresent());
    // Test for simplified canBuy() & canSell().
    // 1 BTC / 150 in my account.
    // 1 BTC = 390 UST.
    final AccountDTO account = strategy.getAccounts().get("03");
    assertNotNull(account);
    strategy.getAccounts().remove("01");
    // Check canBuy().
    // Trying to buy 1 bitcoin for 390 USDT per bitcoin - should work.
    assertTrue(strategy.canBuy(new BigDecimal("1")));
    assertTrue(strategy.canBuy(account, new BigDecimal("1")));
    // Trying to buy 2 bitcoin for 390 USDT per bitcoin - should not work.
    assertFalse(strategy.canBuy(new BigDecimal("2")));
    assertFalse(strategy.canBuy(account, new BigDecimal("2")));
    // Trying to buy 1 bitcoin for 390 USDT per bitcoin, but I want 400 USDT left - should not work.
    assertFalse(strategy.canBuy(new BigDecimal("2"), new BigDecimal("400")));
    assertFalse(strategy.canBuy(account, new BigDecimal("2"), new BigDecimal("400")));
    // Check canSell().
    // 1 BTC / 500 in my account.
    // Wanting to sell 1 bitcoin - I have them.
    assertTrue(strategy.canSell(new BigDecimal("1")));
    assertTrue(strategy.canSell(account, new BigDecimal("1")));
    // Wanting to sell 2 bitcoin - I don't have them.
    assertFalse(strategy.canSell(new BigDecimal("2")));
    assertFalse(strategy.canSell(account, new BigDecimal("2")));
    // Wanting to sell 1 bitcoin but have one left after - Not possible.
    assertFalse(strategy.canSell(new BigDecimal("1"), new BigDecimal('1')));
    assertFalse(strategy.canSell(account, new BigDecimal("1"), new BigDecimal('1')));
}
Also used : CurrencyAmountDTO(tech.cassandre.trading.bot.dto.util.CurrencyAmountDTO) Strategy(tech.cassandre.trading.bot.domain.Strategy) TestableTa4jCassandreStrategy(tech.cassandre.trading.bot.test.util.strategies.TestableTa4jCassandreStrategy) AccountDTO(tech.cassandre.trading.bot.dto.user.AccountDTO) BigDecimal(java.math.BigDecimal) BaseTest(tech.cassandre.trading.bot.test.util.junit.BaseTest) Test(org.junit.jupiter.api.Test) SpringBootTest(org.springframework.boot.test.context.SpringBootTest) DisplayName(org.junit.jupiter.api.DisplayName)

Aggregations

Strategy (tech.cassandre.trading.bot.domain.Strategy)4 BigDecimal (java.math.BigDecimal)3 DisplayName (org.junit.jupiter.api.DisplayName)3 Test (org.junit.jupiter.api.Test)3 SpringBootTest (org.springframework.boot.test.context.SpringBootTest)3 AccountDTO (tech.cassandre.trading.bot.dto.user.AccountDTO)3 BaseTest (tech.cassandre.trading.bot.test.util.junit.BaseTest)3 CurrencyAmountDTO (tech.cassandre.trading.bot.dto.util.CurrencyAmountDTO)2 CurrencyPairDTO (tech.cassandre.trading.bot.dto.util.CurrencyPairDTO)2 HashSet (java.util.HashSet)1 Set (java.util.Set)1 PostConstruct (javax.annotation.PostConstruct)1 TickerDTO (tech.cassandre.trading.bot.dto.market.TickerDTO)1 PositionCreationResultDTO (tech.cassandre.trading.bot.dto.position.PositionCreationResultDTO)1 StrategyDTO (tech.cassandre.trading.bot.dto.strategy.StrategyDTO)1 UserDTO (tech.cassandre.trading.bot.dto.user.UserDTO)1 CassandreStrategy (tech.cassandre.trading.bot.strategy.CassandreStrategy)1 CassandreStrategyInterface (tech.cassandre.trading.bot.strategy.internal.CassandreStrategyInterface)1 TestableCassandreStrategy (tech.cassandre.trading.bot.test.util.strategies.TestableCassandreStrategy)1 TestableTa4jCassandreStrategy (tech.cassandre.trading.bot.test.util.strategies.TestableTa4jCassandreStrategy)1