Search in sources :

Example 1 with GainDTO

use of tech.cassandre.trading.bot.dto.util.GainDTO in project cassandre-trading-bot by cassandre-tech.

the class PositionServiceTest method checkClosePosition.

@Test
@DisplayName("Check close position")
public void checkClosePosition() {
    // =============================================================================================================
    // Creates position 1 (ETH/BTC, 0.0001, 100% stop gain).
    final PositionCreationResultDTO creationResult1 = strategy.createLongPosition(ETH_BTC, new BigDecimal("0.0001"), PositionRulesDTO.builder().stopGainPercentage(100f).build());
    final long position1Uid = creationResult1.getPosition().getUid();
    assertEquals("ORDER00010", creationResult1.getPosition().getOpeningOrder().getOrderId());
    // We retrieve the order from the service, and we wait for the order to update the position.
    // Only one order updates (we don't send the PENDING_NOW order in flux).
    // Two positions updates because order status change from PENDING_NEW to NEW.
    orderFlux.update();
    await().untilAsserted(() -> assertEquals(1, strategy.getOrdersUpdatesReceived().size()));
    await().untilAsserted(() -> assertEquals(2, strategy.getPositionsUpdatesReceived().size()));
    // This trade complets the opening order so the position moves to OPENED status.
    tradeFlux.emitValue(TradeDTO.builder().tradeId("000002").type(BID).orderId("ORDER00010").currencyPair(ETH_BTC).amount(new CurrencyAmountDTO("0.0001", ETH_BTC.getBaseCurrency())).price(new CurrencyAmountDTO("0.2", ETH_BTC.getQuoteCurrency())).build());
    await().untilAsserted(() -> assertEquals(OPENED, getPositionDTO(position1Uid).getStatus()));
    // =============================================================================================================
    // We send tickers.
    // A first ticker arrives with a gain of 100% but for the wrong CP - so it must still be OPENED.
    tickerFlux.emitValue(TickerDTO.builder().currencyPair(ETH_USDT).last(new BigDecimal("0.5")).build());
    await().untilAsserted(() -> assertEquals(3, strategy.getPositionsUpdatesReceived().size()));
    PositionDTO p = getPositionDTO(position1Uid);
    assertEquals(OPENED, p.getStatus());
    // We check the last calculated gain - should be none.
    Optional<GainDTO> gain = p.getLatestCalculatedGain();
    assertFalse(gain.isPresent());
    // A second ticker arrives with a gain of 50%.
    // From 0.2 (trade) to 0.3 (ticker).
    tickerFlux.emitValue(TickerDTO.builder().currencyPair(ETH_BTC).last(new BigDecimal("0.3")).build());
    await().untilAsserted(() -> assertEquals(4, strategy.getPositionsUpdatesReceived().size()));
    p = getPositionDTO(position1Uid);
    // We check the last calculated gain - should be 50%.
    gain = p.getLatestCalculatedGain();
    // Check the calculated gains.
    assertTrue(gain.isPresent());
    assertEquals(50, gain.get().getPercentage());
    assertEquals(0, new BigDecimal("0.00001").compareTo(gain.get().getAmount().getValue()));
    assertEquals(BTC, gain.get().getAmount().getCurrency());
}
Also used : PositionCreationResultDTO(tech.cassandre.trading.bot.dto.position.PositionCreationResultDTO) CurrencyAmountDTO(tech.cassandre.trading.bot.dto.util.CurrencyAmountDTO) GainDTO(tech.cassandre.trading.bot.dto.util.GainDTO) BigDecimal(java.math.BigDecimal) PositionDTO(tech.cassandre.trading.bot.dto.position.PositionDTO) Test(org.junit.jupiter.api.Test) SpringBootTest(org.springframework.boot.test.context.SpringBootTest) BaseTest(tech.cassandre.trading.bot.test.util.junit.BaseTest) DisplayName(org.junit.jupiter.api.DisplayName)

Example 2 with GainDTO

use of tech.cassandre.trading.bot.dto.util.GainDTO in project cassandre-trading-bot by cassandre-tech.

the class GainDTOTest method isSuperiorTo.

@Test
@DisplayName("Check isSuperiorTo() method")
public void isSuperiorTo() {
    GainDTO gain = GainDTO.builder().percentage(2).amount(new CurrencyAmountDTO(new BigDecimal("2"), BTC)).build();
    GainDTO inferiorGain = GainDTO.builder().percentage(1).amount(new CurrencyAmountDTO(new BigDecimal("2"), BTC)).build();
    GainDTO superiorGain = GainDTO.builder().percentage(3).amount(new CurrencyAmountDTO(new BigDecimal("2"), BTC)).build();
    assertTrue(gain.isSuperiorTo(inferiorGain));
    assertFalse(gain.isSuperiorTo(superiorGain));
    assertTrue(gain.isSuperiorTo(GainDTO.ZERO));
}
Also used : CurrencyAmountDTO(tech.cassandre.trading.bot.dto.util.CurrencyAmountDTO) GainDTO(tech.cassandre.trading.bot.dto.util.GainDTO) BigDecimal(java.math.BigDecimal) Test(org.junit.jupiter.api.Test) DisplayName(org.junit.jupiter.api.DisplayName)

Example 3 with GainDTO

use of tech.cassandre.trading.bot.dto.util.GainDTO in project cassandre-trading-bot by cassandre-tech.

the class GainDTOTest method checkIsInferiorTO.

@Test
@DisplayName("Check isInferiorTo() method")
public void checkIsInferiorTO() {
    GainDTO gain = GainDTO.builder().percentage(2).amount(new CurrencyAmountDTO(new BigDecimal("2"), BTC)).build();
    GainDTO inferiorGain = GainDTO.builder().percentage(1).amount(new CurrencyAmountDTO(new BigDecimal("2"), BTC)).build();
    GainDTO superiorGain = GainDTO.builder().percentage(3).amount(new CurrencyAmountDTO(new BigDecimal("2"), BTC)).build();
    assertFalse(gain.isInferiorTo(inferiorGain));
    assertTrue(gain.isInferiorTo(superiorGain));
    assertFalse(gain.isInferiorTo(GainDTO.ZERO));
}
Also used : CurrencyAmountDTO(tech.cassandre.trading.bot.dto.util.CurrencyAmountDTO) GainDTO(tech.cassandre.trading.bot.dto.util.GainDTO) BigDecimal(java.math.BigDecimal) Test(org.junit.jupiter.api.Test) DisplayName(org.junit.jupiter.api.DisplayName)

Example 4 with GainDTO

use of tech.cassandre.trading.bot.dto.util.GainDTO in project cassandre-trading-bot by cassandre-tech.

the class PositionLongFluxTest method checkReceivedData.

@Test
@DisplayName("Check received data")
public void checkReceivedData() {
    // =============================================================================================================
    // Creates long position n°1 - 10 ETH bought with BTC.
    // Position will be closed if 1 000% gain or 100% loss.
    final PositionCreationResultDTO position1Result = strategy.createLongPosition(ETH_BTC, new BigDecimal("10"), PositionRulesDTO.builder().stopGainPercentage(// 1 000% max gain.
    1_000f).stopLossPercentage(// 100% max lost.
    100f).build());
    assertEquals("ORDER00010", position1Result.getPosition().getOpeningOrder().getOrderId());
    long position1Uid = position1Result.getPosition().getUid();
    // onPositionStatusUpdate - Position 1 should arrive (OPENING).
    // 1 position status update:
    // - The position is created with the OPENING status.
    await().untilAsserted(() -> assertEquals(1, strategy.getPositionsStatusUpdatesCount()));
    PositionDTO p = strategy.getLastPositionStatusUpdate();
    assertNotNull(p);
    assertEquals(position1Uid, p.getUid());
    assertEquals(OPENING, p.getStatus());
    assertEquals("Long position n°1 of 10 ETH (rules: 1000.0 % gain / 100.0 % loss) - Opening - Waiting for the trades of order ORDER00010", p.getDescription());
    // onPositionUpdate - Position 1 should arrive (OPENING).
    // 2 positions updates:
    // - Position created with a local order saved in database (Order with status PENDING_NEW).
    // - Position updated with the local order retrieved from getOrders with status NEW.
    await().untilAsserted(() -> assertEquals(2, strategy.getPositionsUpdatesReceived().size()));
    p = strategy.getLastPositionUpdate();
    assertNotNull(p);
    assertEquals(position1Uid, p.getUid());
    assertEquals(OPENING, p.getStatus());
    // Check data in strategy & database.
    assertEquals(1, positionRepository.count());
    assertEquals(1, strategy.getPositions().size());
    Optional<PositionDTO> p1 = strategy.getPositionByPositionId(position1Uid);
    assertTrue(p1.isPresent());
    assertEquals(1, p1.get().getUid());
    assertEquals(1, p1.get().getPositionId());
    assertEquals(LONG, p1.get().getType());
    assertNotNull(p1.get().getStrategy());
    assertEquals(1, p1.get().getStrategy().getUid());
    assertEquals("01", p1.get().getStrategy().getStrategyId());
    assertEquals(ETH_BTC, p1.get().getCurrencyPair());
    assertEquals(0, new BigDecimal("10").compareTo(p1.get().getAmount().getValue()));
    assertEquals(ETH_BTC.getBaseCurrency(), p1.get().getAmount().getCurrency());
    assertTrue(p1.get().getRules().isStopGainPercentageSet());
    assertEquals(1_000f, p1.get().getRules().getStopGainPercentage());
    assertTrue(p1.get().getRules().isStopLossPercentageSet());
    assertEquals(100f, p1.get().getRules().getStopLossPercentage());
    assertEquals(OPENING, p1.get().getStatus());
    assertEquals("ORDER00010", p1.get().getOpeningOrder().getOrderId());
    assertEquals("ORDER00010", p1.get().getOpeningOrder().getOrderId());
    assertTrue(p1.get().getOpeningOrder().getTrades().isEmpty());
    assertNull(p1.get().getClosingOrder());
    assertNull(p1.get().getLowestGainPrice());
    assertNull(p1.get().getHighestGainPrice());
    assertNull(p1.get().getLatestGainPrice());
    // =============================================================================================================
    // Creates long position n°2 - 0.0002 ETH bought with BTC.
    // Position will be closed if 10 000% gain or 10 000% loss.
    final PositionCreationResultDTO position2Result = strategy.createLongPosition(ETH_USDT, new BigDecimal("0.0002"), PositionRulesDTO.builder().stopGainPercentage(10_000f).stopLossPercentage(10_000f).build());
    assertEquals("ORDER00020", position2Result.getPosition().getOpeningOrder().getOrderId());
    long position2Id = position2Result.getPosition().getUid();
    // onPositionStatusUpdate - Position 2 should arrive (OPENING).
    // 1 position status update - The position is created with the OPENING status.
    await().untilAsserted(() -> assertEquals(2, strategy.getPositionsStatusUpdatesCount()));
    p = strategy.getLastPositionStatusUpdate();
    assertNotNull(p);
    assertEquals(position2Id, p.getUid());
    assertEquals(OPENING, p.getStatus());
    assertEquals("Long position n°2 of 0.0002 ETH (rules: 10000.0 % gain / 10000.0 % loss) - Opening - Waiting for the trades of order ORDER00020", p.getDescription());
    // onPositionUpdate - Position 2 should arrive (OPENING).
    // - Position created with a local order saved in database (Order with status PENDING_NEW).
    // - Position updated with the local order retrieved from getOrders with status NEW.
    await().untilAsserted(() -> assertEquals(4, strategy.getPositionsUpdatesCount()));
    p = strategy.getLastPositionUpdate();
    assertNotNull(p);
    assertEquals(position2Id, p.getUid());
    assertEquals(OPENING, p.getStatus());
    // Check data we have in strategy & database.
    assertEquals(2, positionRepository.count());
    assertEquals(2, strategy.getPositions().size());
    Optional<PositionDTO> p2 = strategy.getPositionByPositionId(position2Id);
    assertTrue(p2.isPresent());
    assertEquals(2, p2.get().getUid());
    assertEquals(2, p2.get().getPositionId());
    assertEquals(LONG, p2.get().getType());
    assertNotNull(p2.get().getStrategy());
    assertEquals(1, p2.get().getStrategy().getUid());
    assertEquals("01", p2.get().getStrategy().getStrategyId());
    assertEquals(ETH_USDT, p2.get().getCurrencyPair());
    assertEquals(0, new BigDecimal("0.0002").compareTo(p2.get().getAmount().getValue()));
    assertEquals(ETH_USDT.getBaseCurrency(), p2.get().getAmount().getCurrency());
    assertTrue(p2.get().getRules().isStopGainPercentageSet());
    assertEquals(10_000f, p2.get().getRules().getStopGainPercentage());
    assertTrue(p2.get().getRules().isStopLossPercentageSet());
    assertEquals(10_000f, p2.get().getRules().getStopLossPercentage());
    assertEquals(OPENING, p2.get().getStatus());
    assertEquals("ORDER00020", p2.get().getOpeningOrder().getOrderId());
    assertTrue(p2.get().getOpeningOrder().getTrades().isEmpty());
    assertNull(p2.get().getClosingOrder());
    assertNull(p2.get().getLowestGainPrice());
    assertNull(p2.get().getHighestGainPrice());
    assertNull(p2.get().getLatestGainPrice());
    // =============================================================================================================
    // Position n°1 is buying 10 ETH with BTC.
    // Two trades arrives with 5 ETH each (so the two makes 10 ETH).
    // 11 is before 1 to test the timestamp order of getOpenTrades & getCloseTrades.
    // First trade.
    tradeFlux.emitValue(TradeDTO.builder().tradeId("000011").type(BID).orderId("ORDER00010").currencyPair(ETH_BTC).amount(new CurrencyAmountDTO("5", ETH_BTC.getBaseCurrency())).price(new CurrencyAmountDTO("0.02", ETH_BTC.getQuoteCurrency())).timestamp(createZonedDateTime("02-02-2020")).build());
    // The same trade is emitted two times with an update (on timestamp).
    tradeFlux.emitValue(TradeDTO.builder().tradeId("000011").orderId("ORDER00010").type(BID).currencyPair(ETH_BTC).amount(new CurrencyAmountDTO("5", ETH_BTC.getBaseCurrency())).price(new CurrencyAmountDTO("0.02", ETH_BTC.getQuoteCurrency())).timestamp(createZonedDateTime("03-02-2020")).build());
    // Second trade.
    tradeFlux.emitValue(TradeDTO.builder().tradeId("000001").orderId("ORDER00010").type(BID).currencyPair(ETH_BTC).amount(new CurrencyAmountDTO("5", ETH_BTC.getBaseCurrency())).price(new CurrencyAmountDTO("0.04", ETH_BTC.getQuoteCurrency())).timestamp(createZonedDateTime("01-01-2020")).build());
    // The same trade is emitted two times with an update (on timestamp).
    tradeFlux.emitValue(TradeDTO.builder().tradeId("000001").orderId("ORDER00010").type(BID).currencyPair(ETH_BTC).amount(new CurrencyAmountDTO("5", ETH_BTC.getBaseCurrency())).price(new CurrencyAmountDTO("0.04", ETH_BTC.getQuoteCurrency())).timestamp(createZonedDateTime("02-01-2020")).build());
    // onPositionStatusUpdate - Position 1 should change to OPENED.
    // With the two trades emitted, status should change to OPENED.
    await().untilAsserted(() -> assertEquals(3, strategy.getPositionsStatusUpdatesCount()));
    p = strategy.getLastPositionStatusUpdate();
    assertNotNull(p);
    assertEquals(position1Uid, p.getUid());
    assertEquals(OPENED, p.getStatus());
    assertEquals("Long position n°1 of 10 ETH (rules: 1000.0 % gain / 100.0 % loss) - Opened", p.getDescription());
    // onPositionUpdate - 2 trades emitted 2 times so 4 updates (+4 already received for position opening).
    // We were at 4 first.
    // Trade 000011 arrives with 5 ETH.
    // Trade 000011 arrives with timestamp updated.
    // Trade 000001 arrives with 5 ETH.
    // Trade 000001 arrives with timestamp updated.
    // Now we have 8 updates.
    await().untilAsserted(() -> assertEquals(8, strategy.getPositionsUpdatesCount()));
    p = strategy.getLastPositionUpdate();
    assertNotNull(p);
    assertEquals(position1Uid, p.getUid());
    assertEquals(OPENED, p.getStatus());
    // Checking what we have in database.
    assertEquals(2, strategy.getPositions().size());
    p1 = strategy.getPositionByPositionId(position1Uid);
    assertTrue(p1.isPresent());
    assertEquals(1, p1.get().getUid());
    assertEquals(1, p1.get().getPositionId());
    assertEquals(LONG, p1.get().getType());
    assertNotNull(p1.get().getStrategy());
    assertEquals(1, p1.get().getStrategy().getUid());
    assertEquals("01", p1.get().getStrategy().getStrategyId());
    assertEquals(ETH_BTC, p1.get().getCurrencyPair());
    assertEquals(0, new BigDecimal("10").compareTo(p1.get().getAmount().getValue()));
    assertEquals(ETH_BTC.getBaseCurrency(), p1.get().getAmount().getCurrency());
    assertTrue(p1.get().getRules().isStopGainPercentageSet());
    assertEquals(1_000f, p1.get().getRules().getStopGainPercentage());
    assertTrue(p1.get().getRules().isStopLossPercentageSet());
    assertEquals(100f, p1.get().getRules().getStopLossPercentage());
    assertEquals(OPENED, p1.get().getStatus());
    assertEquals("ORDER00010", p1.get().getOpeningOrder().getOrderId());
    Iterator<TradeDTO> openingTradesIterator = p1.get().getOpeningOrder().getTrades().iterator();
    assertEquals("000001", openingTradesIterator.next().getTradeId());
    assertEquals("000011", openingTradesIterator.next().getTradeId());
    assertNull(p1.get().getClosingOrder());
    assertNull(p1.get().getLowestGainPrice());
    assertNull(p1.get().getHighestGainPrice());
    assertNull(p1.get().getLatestGainPrice());
    // Check if we don't have duplicated trades in database !
    assertEquals(2, tradeRepository.count());
    Optional<Order> order00010 = orderRepository.findByOrderId("ORDER00010");
    assertTrue(order00010.isPresent());
    assertEquals(2, order00010.get().getTrades().size());
    // =============================================================================================================
    // Test of tickers updating position n°1.
    // From the trades, we had:
    // 000011 = 5 * 0.02
    // 000001 = 5 * 0.04
    // so the price is 0.03.
    // First ticker arrives (500% gain) - min, max and last gain should be set to that value.
    // Price update so a new position update.
    tickerFlux.emitValue(TickerDTO.builder().currencyPair(ETH_BTC).last(new BigDecimal("0.18")).build());
    await().untilAsserted(() -> assertEquals(9, strategy.getPositionsUpdatesCount()));
    p = strategy.getLastPositionUpdate();
    assertEquals(position1Uid, p.getUid());
    assertEquals(0, new BigDecimal("0.18").compareTo(p.getLowestGainPrice().getValue()));
    assertEquals(0, new BigDecimal("0.18").compareTo(p.getHighestGainPrice().getValue()));
    assertEquals(0, new BigDecimal("0.18").compareTo(p.getLatestGainPrice().getValue()));
    assertEquals("Long position n°1 of 10 ETH (rules: 1000.0 % gain / 100.0 % loss) - Opened - Last gain calculated 500 %", p.getDescription());
    // We check the gain.
    Optional<GainDTO> latestCalculatedGain = p.getLatestCalculatedGain();
    assertTrue(latestCalculatedGain.isPresent());
    assertEquals(500, latestCalculatedGain.get().getPercentage());
    assertEquals(0, new BigDecimal("1.5").compareTo(latestCalculatedGain.get().getAmount().getValue()));
    assertEquals(BTC, latestCalculatedGain.get().getAmount().getCurrency());
    // Second ticker arrives (100% gain) - min and last gain should be set to that value.
    // Price update so a new position update.
    tickerFlux.emitValue(TickerDTO.builder().currencyPair(ETH_BTC).last(new BigDecimal("0.06")).build());
    await().untilAsserted(() -> assertEquals(10, strategy.getPositionsUpdatesCount()));
    p = strategy.getLastPositionUpdate();
    assertEquals(position1Uid, p.getUid());
    assertEquals(0, new BigDecimal("0.06").compareTo(p.getLowestGainPrice().getValue()));
    assertEquals(0, new BigDecimal("0.18").compareTo(p.getHighestGainPrice().getValue()));
    assertEquals(0, new BigDecimal("0.06").compareTo(p.getLatestGainPrice().getValue()));
    // We check the gain.
    latestCalculatedGain = p.getLatestCalculatedGain();
    assertTrue(latestCalculatedGain.isPresent());
    assertEquals(100, latestCalculatedGain.get().getPercentage());
    assertEquals(0, new BigDecimal("0.3").compareTo(latestCalculatedGain.get().getAmount().getValue()));
    assertEquals(BTC, latestCalculatedGain.get().getAmount().getCurrency());
    // Third ticker arrives (200% gain) - only last should change.
    // Price update so a new position update.
    tickerFlux.emitValue(TickerDTO.builder().currencyPair(ETH_BTC).last(new BigDecimal("0.09")).build());
    await().untilAsserted(() -> assertEquals(11, strategy.getPositionsUpdatesCount()));
    p = strategy.getLastPositionUpdate();
    assertEquals(position1Uid, p.getUid());
    assertEquals(0, new BigDecimal("0.06").compareTo(p.getLowestGainPrice().getValue()));
    assertEquals(0, new BigDecimal("0.18").compareTo(p.getHighestGainPrice().getValue()));
    assertEquals(0, new BigDecimal("0.09").compareTo(p.getLatestGainPrice().getValue()));
    // Fourth ticker arrives (50% loss) - min and last gain should be set to that value.
    // Price update so a new position update.
    tickerFlux.emitValue(TickerDTO.builder().currencyPair(ETH_BTC).last(new BigDecimal("0.015")).build());
    await().untilAsserted(() -> assertEquals(12, strategy.getPositionsUpdatesCount()));
    p = strategy.getLastPositionUpdate();
    assertEquals(position1Uid, p.getUid());
    assertEquals(0, new BigDecimal("0.015").compareTo(p.getLowestGainPrice().getValue()));
    assertEquals(0, new BigDecimal("0.18").compareTo(p.getHighestGainPrice().getValue()));
    assertEquals(0, new BigDecimal("0.015").compareTo(p.getLatestGainPrice().getValue()));
    // A ticker arrive for another cp. Nothing should change.
    // And no position update.
    tickerFlux.emitValue(TickerDTO.builder().currencyPair(ETH_USDT).last(new BigDecimal("100")).build());
    // Fifth ticker arrives (600% gain) - max and last gain should be set to that value.
    // Price update so a new position update.
    tickerFlux.emitValue(TickerDTO.builder().currencyPair(ETH_BTC).last(new BigDecimal("0.21")).build());
    await().untilAsserted(() -> assertEquals(13, strategy.getPositionsUpdatesCount()));
    p = strategy.getLastPositionUpdate();
    assertEquals(position1Uid, p.getUid());
    assertEquals(0, new BigDecimal("0.015").compareTo(p.getLowestGainPrice().getValue()));
    assertEquals(0, new BigDecimal("0.21").compareTo(p.getHighestGainPrice().getValue()));
    assertEquals(0, new BigDecimal("0.21").compareTo(p.getLatestGainPrice().getValue()));
    // Checking what we have in database.
    assertEquals(2, strategy.getPositions().size());
    p1 = strategy.getPositionByPositionId(position1Uid);
    assertTrue(p1.isPresent());
    assertEquals(1, p1.get().getUid());
    assertEquals(1, p1.get().getPositionId());
    assertEquals(LONG, p1.get().getType());
    assertNotNull(p1.get().getStrategy());
    assertEquals(1, p1.get().getStrategy().getUid());
    assertEquals("01", p1.get().getStrategy().getStrategyId());
    assertEquals(ETH_BTC, p1.get().getCurrencyPair());
    assertEquals(0, new BigDecimal("10").compareTo(p1.get().getAmount().getValue()));
    assertEquals(ETH_BTC.getBaseCurrency(), p1.get().getAmount().getCurrency());
    assertTrue(p1.get().getRules().isStopGainPercentageSet());
    assertEquals(1_000f, p1.get().getRules().getStopGainPercentage());
    assertTrue(p1.get().getRules().isStopLossPercentageSet());
    assertEquals(100f, p1.get().getRules().getStopLossPercentage());
    assertEquals(OPENED, p1.get().getStatus());
    assertEquals("ORDER00010", p1.get().getOpeningOrder().getOrderId());
    openingTradesIterator = p1.get().getOpeningOrder().getTrades().iterator();
    assertEquals("000001", openingTradesIterator.next().getTradeId());
    assertEquals("000011", openingTradesIterator.next().getTradeId());
    assertNull(p1.get().getClosingOrder());
    assertEquals(0, new BigDecimal("0.015").compareTo(p1.get().getLowestGainPrice().getValue()));
    assertEquals(0, new BigDecimal("0.21").compareTo(p1.get().getHighestGainPrice().getValue()));
    assertEquals(0, new BigDecimal("0.21").compareTo(p1.get().getLatestGainPrice().getValue()));
    // =============================================================================================================
    // Trade arrives for position 2 - should now be OPENED
    tradeFlux.emitValue(TradeDTO.builder().tradeId("000002").type(BID).orderId("ORDER00020").currencyPair(ETH_USDT).amount(new CurrencyAmountDTO("0.0002", ETH_USDT.getBaseCurrency())).price(new CurrencyAmountDTO("0.03", ETH_USDT.getQuoteCurrency())).build());
    // onPositionStatusUpdate - Position 2 should be opened.
    // - Update 1 : position n°1 OPENING.
    // - Update 2 : position n°2 OPENING.
    // - Update 3 : position n°1 OPENED.
    // - Update 4 : position n°2 OPENED.
    await().untilAsserted(() -> assertEquals(4, strategy.getPositionsStatusUpdatesCount()));
    p = strategy.getLastPositionStatusUpdate();
    assertNotNull(p);
    assertEquals(position2Id, p.getUid());
    assertEquals(OPENED, p.getStatus());
    assertEquals("Long position n°2 of 0.0002 ETH (rules: 10000.0 % gain / 10000.0 % loss) - Opened", p.getDescription());
    // onPositionUpdate.
    // One trade arrives, so we have a position update because of this trade.
    await().untilAsserted(() -> assertEquals(14, strategy.getPositionsUpdatesCount()));
    p = strategy.getLastPositionUpdate();
    assertNotNull(p);
    assertEquals(position2Id, p.getUid());
    assertEquals(OPENED, p.getStatus());
    // Checking what we have in database.
    assertEquals(2, strategy.getPositions().size());
    p2 = strategy.getPositionByPositionId(position2Id);
    assertTrue(p2.isPresent());
    assertEquals(2, p2.get().getUid());
    assertEquals(2, p2.get().getPositionId());
    assertEquals(LONG, p2.get().getType());
    assertNotNull(p2.get().getStrategy());
    assertEquals(1, p2.get().getStrategy().getUid());
    assertEquals("01", p2.get().getStrategy().getStrategyId());
    assertEquals(ETH_USDT, p2.get().getCurrencyPair());
    assertEquals(0, new BigDecimal("0.0002").compareTo(p2.get().getAmount().getValue()));
    assertEquals(ETH_USDT.getBaseCurrency(), p2.get().getAmount().getCurrency());
    assertTrue(p2.get().getRules().isStopGainPercentageSet());
    assertEquals(10_000f, p2.get().getRules().getStopGainPercentage());
    assertTrue(p2.get().getRules().isStopLossPercentageSet());
    assertEquals(10_000f, p2.get().getRules().getStopLossPercentage());
    assertEquals(OPENED, p2.get().getStatus());
    assertEquals("ORDER00020", p2.get().getOpeningOrder().getOrderId());
    openingTradesIterator = p2.get().getOpeningOrder().getTrades().iterator();
    assertEquals("000002", openingTradesIterator.next().getTradeId());
    assertNull(p2.get().getClosingOrder());
    assertNull(p2.get().getLowestGainPrice());
    assertNull(p2.get().getHighestGainPrice());
    assertNull(p2.get().getLatestGainPrice());
    // =============================================================================================================
    // A ticker arrives that triggers max gain rules of position 1 - should now be CLOSING.
    // From the trades, we had:
    // 000011 = 5 * 0.02
    // 000001 = 5 * 0.04
    // The mean price is 0.03 BTC, and we now receive a new price of 100.
    tickerFlux.emitValue(TickerDTO.builder().currencyPair(ETH_BTC).last(new BigDecimal("100")).build());
    // onPositionStatusUpdate - Position 1 should be closing.
    // - Update 1 : position n°1 OPENING.
    // - Update 2 : position n°2 OPENING.
    // - Update 3 : position n°1 OPENED.
    // - Update 4 : position n°2 OPENED.
    // - Update 5 : position n°1 CLOSING.
    await().untilAsserted(() -> assertEquals(5, strategy.getPositionsStatusUpdatesCount()));
    p = strategy.getLastPositionStatusUpdate();
    assertNotNull(p);
    assertEquals(position1Uid, p.getUid());
    assertEquals(CLOSING, p.getStatus());
    assertEquals("Long position n°1 of 10 ETH (rules: 1000.0 % gain / 100.0 % loss) - Closing - Waiting for the trades of order ORDER00011", p.getDescription());
    // OnPositionUpdate - We were having 14 updates.
    // - A ticker triggering position closure arrives.
    // - Position closed with the local order (status PENDING_NEW).
    // - Position updated with the distant order (status NEW).
    await().untilAsserted(() -> assertEquals(17, strategy.getPositionsUpdatesCount()));
    p = strategy.getLastPositionUpdate();
    assertNotNull(p);
    assertEquals(position1Uid, p.getUid());
    assertEquals(CLOSING, p.getStatus());
    // Checking what we have in database.
    assertEquals(2, strategy.getPositions().size());
    p1 = strategy.getPositionByPositionId(position1Uid);
    assertTrue(p1.isPresent());
    assertEquals(1, p1.get().getUid());
    assertEquals(1, p1.get().getPositionId());
    assertEquals(LONG, p1.get().getType());
    assertNotNull(p1.get().getStrategy());
    assertEquals(1, p1.get().getStrategy().getUid());
    assertEquals("01", p1.get().getStrategy().getStrategyId());
    assertEquals(ETH_BTC, p1.get().getCurrencyPair());
    assertEquals(0, new BigDecimal("10").compareTo(p1.get().getAmount().getValue()));
    assertEquals(ETH_BTC.getBaseCurrency(), p1.get().getAmount().getCurrency());
    assertTrue(p1.get().getRules().isStopGainPercentageSet());
    assertEquals(1_000f, p1.get().getRules().getStopGainPercentage());
    assertTrue(p1.get().getRules().isStopLossPercentageSet());
    assertEquals(100f, p1.get().getRules().getStopLossPercentage());
    assertEquals(CLOSING, p1.get().getStatus());
    assertEquals("ORDER00010", p1.get().getOpeningOrder().getOrderId());
    openingTradesIterator = p1.get().getOpeningOrder().getTrades().iterator();
    assertEquals("000001", openingTradesIterator.next().getTradeId());
    assertEquals("000011", openingTradesIterator.next().getTradeId());
    assertEquals("ORDER00011", p1.get().getClosingOrder().getOrderId());
    assertTrue(p1.get().getClosingOrder().getTrades().isEmpty());
    assertEquals(0, new BigDecimal("0.015").compareTo(p1.get().getLowestGainPrice().getValue()));
    assertEquals(0, new BigDecimal("0.21").compareTo(p1.get().getHighestGainPrice().getValue()));
    assertEquals(0, new BigDecimal("100").compareTo(p1.get().getLatestGainPrice().getValue()));
    // =============================================================================================================
    // Position 1 will move to CLOSED status when the trades arrive.
    // The first close trade arrives but the amount is not enough.
    tradeFlux.emitValue(TradeDTO.builder().tradeId("000003").orderId("ORDER00011").type(ASK).currencyPair(ETH_BTC).amount(new CurrencyAmountDTO("5", ETH_BTC.getBaseCurrency())).price(new CurrencyAmountDTO("1", ETH_BTC.getQuoteCurrency())).timestamp(createZonedDateTime("01-01-2020")).build());
    // We send a duplicated value.
    tradeFlux.emitValue(TradeDTO.builder().tradeId("000003").orderId("ORDER00011").type(ASK).currencyPair(ETH_BTC).amount(new CurrencyAmountDTO("5", ETH_BTC.getBaseCurrency())).price(new CurrencyAmountDTO("1", ETH_BTC.getQuoteCurrency())).timestamp(createZonedDateTime("01-01-2020")).build());
    // onPosition for first trade arrival.
    // Two new updates : the two trades received (even if they were the same as we use emit method).
    await().untilAsserted(() -> assertEquals(19, strategy.getPositionsUpdatesCount()));
    p = strategy.getLastPositionUpdate();
    assertNotNull(p);
    assertEquals(position1Uid, p.getUid());
    assertEquals(CLOSING, p.getStatus());
    // The second close trade arrives now closed.
    tradeFlux.emitValue(TradeDTO.builder().tradeId("000004").orderId("ORDER00011").type(ASK).currencyPair(ETH_BTC).amount(new CurrencyAmountDTO("5", ETH_BTC.getBaseCurrency())).price(new CurrencyAmountDTO("1", ETH_BTC.getQuoteCurrency())).timestamp(createZonedDateTime("02-01-2020")).build());
    // onPositionStatusUpdate - Position should be closed.
    // - Update 1 : position n°1 OPENING.
    // - Update 2 : position n°2 OPENING.
    // - Update 3 : position n°1 OPENED.
    // - Update 4 : position n°2 OPENED.
    // - Update 5 : position n°1 CLOSING.
    // - Update 6 : position n°1 CLOSED.
    await().untilAsserted(() -> assertEquals(6, strategy.getPositionsStatusUpdatesCount()));
    p = strategy.getLastPositionStatusUpdate();
    assertNotNull(p);
    assertEquals(position1Uid, p.getUid());
    assertEquals(CLOSED, p.getStatus());
    assertEquals("Long position n°1 of 10 ETH (rules: 1000.0 % gain / 100.0 % loss) - Closed - Gains: 9.7 BTC (3233.33 %)", p.getDescription());
    // onPositionUpdate.
    // - Trade 000004 arrives.
    await().untilAsserted(() -> assertEquals(20, strategy.getPositionsUpdatesCount()));
    p = strategy.getLastPositionUpdate();
    assertNotNull(p);
    assertEquals(position1Uid, p.getUid());
    assertEquals(CLOSED, p.getStatus());
    // Checking what we have in database.
    assertEquals(2, strategy.getPositions().size());
    p1 = strategy.getPositionByPositionId(position1Uid);
    assertTrue(p1.isPresent());
    assertEquals(1, p1.get().getUid());
    assertEquals(1, p1.get().getPositionId());
    assertEquals(LONG, p1.get().getType());
    assertNotNull(p1.get().getStrategy());
    assertEquals(1, p1.get().getStrategy().getUid());
    assertEquals("01", p1.get().getStrategy().getStrategyId());
    assertEquals(ETH_BTC, p1.get().getCurrencyPair());
    assertEquals(0, new BigDecimal("10").compareTo(p1.get().getAmount().getValue()));
    assertEquals(ETH_BTC.getBaseCurrency(), p1.get().getAmount().getCurrency());
    assertTrue(p1.get().getRules().isStopGainPercentageSet());
    assertEquals(1_000f, p1.get().getRules().getStopGainPercentage());
    assertTrue(p1.get().getRules().isStopLossPercentageSet());
    assertEquals(100f, p1.get().getRules().getStopLossPercentage());
    assertEquals(CLOSED, p1.get().getStatus());
    assertEquals("ORDER00010", p1.get().getOpeningOrder().getOrderId());
    openingTradesIterator = p1.get().getOpeningOrder().getTrades().iterator();
    assertEquals("000001", openingTradesIterator.next().getTradeId());
    assertEquals("000011", openingTradesIterator.next().getTradeId());
    assertEquals("ORDER00011", p1.get().getClosingOrder().getOrderId());
    final Iterator<TradeDTO> closingTradesIterator = p1.get().getClosingOrder().getTrades().iterator();
    assertEquals("000003", closingTradesIterator.next().getTradeId());
    assertEquals("000004", closingTradesIterator.next().getTradeId());
    assertEquals(0, new BigDecimal("0.015").compareTo(p1.get().getLowestGainPrice().getValue()));
    assertEquals(0, new BigDecimal("0.21").compareTo(p1.get().getHighestGainPrice().getValue()));
    assertEquals(0, new BigDecimal("100").compareTo(p1.get().getLatestGainPrice().getValue()));
    // Just checking trades creation.
    assertNotNull(strategy.getPositionByPositionId(position1Uid));
    assertNotNull(strategy.getPositionByPositionId(position2Id));
    assertEquals(5, strategy.getTrades().size());
    // Check if we don't have duplicated trades in database !
    assertEquals(5, tradeRepository.count());
    order00010 = orderRepository.findByOrderId("ORDER00010");
    assertTrue(order00010.isPresent());
    assertEquals(2, order00010.get().getTrades().size());
    final Optional<Order> order00011 = orderRepository.findByOrderId("ORDER00011");
    assertTrue(order00011.isPresent());
    assertEquals(2, order00011.get().getTrades().size());
}
Also used : Order(tech.cassandre.trading.bot.domain.Order) PositionCreationResultDTO(tech.cassandre.trading.bot.dto.position.PositionCreationResultDTO) CurrencyAmountDTO(tech.cassandre.trading.bot.dto.util.CurrencyAmountDTO) TradeDTO(tech.cassandre.trading.bot.dto.trade.TradeDTO) GainDTO(tech.cassandre.trading.bot.dto.util.GainDTO) BigDecimal(java.math.BigDecimal) PositionDTO(tech.cassandre.trading.bot.dto.position.PositionDTO) 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 5 with GainDTO

use of tech.cassandre.trading.bot.dto.util.GainDTO in project cassandre-trading-bot by cassandre-tech.

the class PositionServiceCassandreImplementation method getGains.

@Override
public final Map<CurrencyDTO, GainDTO> getGains(final long strategyUid) {
    logger.debug("Retrieving gains for all positions");
    HashMap<CurrencyDTO, BigDecimal> totalBefore = new LinkedHashMap<>();
    HashMap<CurrencyDTO, BigDecimal> totalAfter = new LinkedHashMap<>();
    List<CurrencyAmountDTO> openingOrdersFees = new LinkedList<>();
    List<CurrencyAmountDTO> closingOrdersFees = new LinkedList<>();
    HashMap<CurrencyDTO, GainDTO> gains = new LinkedHashMap<>();
    // We calculate, by currency, the amount bought & sold.
    positionRepository.findByStatus(CLOSED).stream().filter(position -> strategyUid == 0 || position.getStrategy().getUid() == strategyUid).map(POSITION_MAPPER::mapToPositionDTO).forEach(positionDTO -> {
        // We retrieve the currency and initiate the maps if they are empty
        CurrencyDTO currency;
        if (positionDTO.getType() == LONG) {
            // LONG.
            currency = positionDTO.getCurrencyPair().getQuoteCurrency();
        } else {
            // SHORT.
            currency = positionDTO.getCurrencyPair().getBaseCurrency();
        }
        gains.putIfAbsent(currency, null);
        totalBefore.putIfAbsent(currency, ZERO);
        totalAfter.putIfAbsent(currency, ZERO);
        // We calculate the amounts bought and amount sold.
        if (positionDTO.getType() == LONG) {
            totalBefore.put(currency, positionDTO.getOpeningOrder().getTrades().stream().map(t -> t.getAmountValue().multiply(t.getPriceValue())).reduce(totalBefore.get(currency), BigDecimal::add));
            totalAfter.put(currency, positionDTO.getClosingOrder().getTrades().stream().map(t -> t.getAmountValue().multiply(t.getPriceValue())).reduce(totalAfter.get(currency), BigDecimal::add));
        } else {
            totalBefore.put(currency, positionDTO.getOpeningOrder().getTrades().stream().map(TradeDTO::getAmountValue).reduce(totalBefore.get(currency), BigDecimal::add));
            totalAfter.put(currency, positionDTO.getClosingOrder().getTrades().stream().map(TradeDTO::getAmountValue).reduce(totalAfter.get(currency), BigDecimal::add));
        }
        // And now the fees.
        positionDTO.getOpeningOrder().getTrades().stream().filter(tradeDTO -> tradeDTO.getFee() != null).forEach(tradeDTO -> openingOrdersFees.add(tradeDTO.getFee()));
        positionDTO.getClosingOrder().getTrades().stream().filter(tradeDTO -> tradeDTO.getFee() != null).forEach(tradeDTO -> closingOrdersFees.add(tradeDTO.getFee()));
    });
    gains.keySet().forEach(currency -> {
        // We make the calculation.
        BigDecimal before = totalBefore.get(currency);
        BigDecimal after = totalAfter.get(currency);
        BigDecimal gainAmount = after.subtract(before);
        BigDecimal gainPercentage = ((after.subtract(before)).divide(before, HALF_UP)).multiply(ONE_HUNDRED_BIG_DECIMAL);
        GainDTO g = GainDTO.builder().percentage(gainPercentage.setScale(2, HALF_UP).doubleValue()).amount(CurrencyAmountDTO.builder().value(gainAmount).currency(currency).build()).openingOrderFees(openingOrdersFees).closingOrderFees(closingOrdersFees).build();
        gains.put(currency, g);
    });
    return gains;
}
Also used : RequiredArgsConstructor(lombok.RequiredArgsConstructor) Position(tech.cassandre.trading.bot.domain.Position) CLOSED(tech.cassandre.trading.bot.dto.position.PositionStatusDTO.CLOSED) HashMap(java.util.HashMap) CurrencyAmountDTO(tech.cassandre.trading.bot.dto.util.CurrencyAmountDTO) LONG(tech.cassandre.trading.bot.dto.position.PositionTypeDTO.LONG) LinkedHashMap(java.util.LinkedHashMap) CassandreStrategyInterface(tech.cassandre.trading.bot.strategy.internal.CassandreStrategyInterface) BigDecimal(java.math.BigDecimal) OPENING(tech.cassandre.trading.bot.dto.position.PositionStatusDTO.OPENING) PositionFlux(tech.cassandre.trading.bot.batch.PositionFlux) Locale(java.util.Locale) Map(java.util.Map) CurrencyPairDTO(tech.cassandre.trading.bot.dto.util.CurrencyPairDTO) SHORT(tech.cassandre.trading.bot.dto.position.PositionTypeDTO.SHORT) LinkedList(java.util.LinkedList) FLOOR(java.math.RoundingMode.FLOOR) LinkedHashSet(java.util.LinkedHashSet) OPENED(tech.cassandre.trading.bot.dto.position.PositionStatusDTO.OPENED) PositionDTO(tech.cassandre.trading.bot.dto.position.PositionDTO) BIGINTEGER_SCALE(tech.cassandre.trading.bot.util.math.MathConstants.BIGINTEGER_SCALE) CurrencyDTO(tech.cassandre.trading.bot.dto.util.CurrencyDTO) CassandreStrategy(tech.cassandre.trading.bot.strategy.internal.CassandreStrategy) NonNull(lombok.NonNull) PositionRepository(tech.cassandre.trading.bot.repository.PositionRepository) HALF_UP(java.math.RoundingMode.HALF_UP) GainDTO(tech.cassandre.trading.bot.dto.util.GainDTO) Set(java.util.Set) ZERO(java.math.BigDecimal.ZERO) TradeDTO(tech.cassandre.trading.bot.dto.trade.TradeDTO) Collectors(java.util.stream.Collectors) PositionCreationResultDTO(tech.cassandre.trading.bot.dto.position.PositionCreationResultDTO) PositionTypeDTO(tech.cassandre.trading.bot.dto.position.PositionTypeDTO) List(java.util.List) PositionRulesDTO(tech.cassandre.trading.bot.dto.position.PositionRulesDTO) Stream(java.util.stream.Stream) BaseService(tech.cassandre.trading.bot.util.base.service.BaseService) ONE_HUNDRED_BIG_DECIMAL(tech.cassandre.trading.bot.util.math.MathConstants.ONE_HUNDRED_BIG_DECIMAL) OrderCreationResultDTO(tech.cassandre.trading.bot.dto.trade.OrderCreationResultDTO) Optional(java.util.Optional) TickerDTO(tech.cassandre.trading.bot.dto.market.TickerDTO) CurrencyAmountDTO(tech.cassandre.trading.bot.dto.util.CurrencyAmountDTO) TradeDTO(tech.cassandre.trading.bot.dto.trade.TradeDTO) GainDTO(tech.cassandre.trading.bot.dto.util.GainDTO) CurrencyDTO(tech.cassandre.trading.bot.dto.util.CurrencyDTO) BigDecimal(java.math.BigDecimal) LinkedList(java.util.LinkedList) LinkedHashMap(java.util.LinkedHashMap)

Aggregations

GainDTO (tech.cassandre.trading.bot.dto.util.GainDTO)14 BigDecimal (java.math.BigDecimal)13 CurrencyAmountDTO (tech.cassandre.trading.bot.dto.util.CurrencyAmountDTO)11 DisplayName (org.junit.jupiter.api.DisplayName)9 Test (org.junit.jupiter.api.Test)9 PositionDTO (tech.cassandre.trading.bot.dto.position.PositionDTO)8 SpringBootTest (org.springframework.boot.test.context.SpringBootTest)6 TradeDTO (tech.cassandre.trading.bot.dto.trade.TradeDTO)6 ZERO (java.math.BigDecimal.ZERO)5 Map (java.util.Map)5 Optional (java.util.Optional)5 PositionCreationResultDTO (tech.cassandre.trading.bot.dto.position.PositionCreationResultDTO)5 CurrencyDTO (tech.cassandre.trading.bot.dto.util.CurrencyDTO)5 FLOOR (java.math.RoundingMode.FLOOR)4 List (java.util.List)4 TickerDTO (tech.cassandre.trading.bot.dto.market.TickerDTO)4 LONG (tech.cassandre.trading.bot.dto.position.PositionTypeDTO.LONG)4 CurrencyPairDTO (tech.cassandre.trading.bot.dto.util.CurrencyPairDTO)4 HALF_UP (java.math.RoundingMode.HALF_UP)3 HashMap (java.util.HashMap)3