Search in sources :

Example 1 with PositionDTO

use of tech.cassandre.trading.bot.dto.position.PositionDTO in project cassandre-trading-bot by cassandre-tech.

the class PositionServiceCassandreImplementation method createPosition.

/**
 * Creates a position.
 *
 * @param strategy     strategy
 * @param type         long or short
 * @param currencyPair currency pair
 * @param amount       amount
 * @param rules        rules
 * @return position creation result
 */
public final PositionCreationResultDTO createPosition(final GenericCassandreStrategy strategy, final PositionTypeDTO type, final CurrencyPairDTO currencyPair, final BigDecimal amount, final PositionRulesDTO rules) {
    logger.debug("Creating a {} position for {} on {} with the rules: {}", type.toString().toLowerCase(Locale.ROOT), amount, currencyPair, rules);
    // It's forbidden to create a position with a too small amount.
    if (amount == null || MINIMUM_AMOUNT_FOR_POSITION.compareTo(amount) > 0) {
        logger.error("Impossible to create a position for such a small amount");
        return new PositionCreationResultDTO("Impossible to create a position for such a small amount: " + amount, null);
    }
    // =============================================================================================================
    // Creates the order.
    final OrderCreationResultDTO orderCreationResult;
    if (type == LONG) {
        // Long position - we buy.
        orderCreationResult = tradeService.createBuyMarketOrder(strategy, currencyPair, amount);
    } else {
        // Short position - we sell.
        orderCreationResult = tradeService.createSellMarketOrder(strategy, currencyPair, amount);
    }
    // If it works, creates the position.
    if (orderCreationResult.isSuccessful()) {
        // =========================================================================================================
        // Creates the position in database.
        Position position = new Position();
        position.setStrategy(STRATEGY_MAPPER.mapToStrategy(strategy.getStrategyDTO()));
        position = positionRepository.save(position);
        // =========================================================================================================
        // Creates the position dto.
        PositionDTO p = new PositionDTO(position.getId(), type, strategy.getStrategyDTO(), currencyPair, amount, orderCreationResult.getOrder(), rules);
        positionRepository.save(POSITION_MAPPER.mapToPosition(p));
        logger.debug("Position {} opened with order {}", p.getPositionId(), orderCreationResult.getOrder().getOrderId());
        // =========================================================================================================
        // Creates the result & emit the position.
        positionFlux.emitValue(p);
        return new PositionCreationResultDTO(p);
    } else {
        logger.error("Position creation failure: {}", orderCreationResult.getErrorMessage());
        return new PositionCreationResultDTO(orderCreationResult.getErrorMessage(), orderCreationResult.getException());
    }
}
Also used : PositionCreationResultDTO(tech.cassandre.trading.bot.dto.position.PositionCreationResultDTO) Position(tech.cassandre.trading.bot.domain.Position) OrderCreationResultDTO(tech.cassandre.trading.bot.dto.trade.OrderCreationResultDTO) PositionDTO(tech.cassandre.trading.bot.dto.position.PositionDTO)

Example 2 with PositionDTO

use of tech.cassandre.trading.bot.dto.position.PositionDTO in project cassandre-trading-bot by cassandre-tech.

the class PositionServiceCassandreImplementation method closePosition.

@Override
public final OrderCreationResultDTO closePosition(final GenericCassandreStrategy strategy, final long id, final TickerDTO ticker) {
    final Optional<Position> position = positionRepository.findById(id);
    if (position.isPresent()) {
        final PositionDTO positionDTO = POSITION_MAPPER.mapToPositionDTO(position.get());
        final OrderCreationResultDTO orderCreationResult;
        if (positionDTO.getType() == LONG) {
            // Long - We just sell.
            orderCreationResult = tradeService.createSellMarketOrder(strategy, positionDTO.getCurrencyPair(), positionDTO.getAmount().getValue());
        } else {
            // Short - We buy back with the money we get from the original selling.
            // On opening, we had:
            // CP2: ETH/USDT - 1 ETH costs 10 USDT - We sold 1 ETH, and it will give us 10 USDT.
            // We will use those 10 USDT to buy back ETH when the rule is triggered.
            // CP2: ETH/USDT - 1 ETH costs 2 USDT - We buy 5 ETH, and it will cost us 10 USDT.
            // We can now use those 10 USDT to buy 5 ETH (amount sold / price).
            final BigDecimal amountToBuy = positionDTO.getAmountToLock().getValue().divide(ticker.getLast(), HALF_UP).setScale(SCALE, FLOOR);
            orderCreationResult = tradeService.createBuyMarketOrder(strategy, positionDTO.getCurrencyPair(), amountToBuy);
        }
        if (orderCreationResult.isSuccessful()) {
            positionDTO.closePositionWithOrder(orderCreationResult.getOrder());
            logger.debug("Position {} closed with order {}", positionDTO.getPositionId(), orderCreationResult.getOrder().getOrderId());
        } else {
            logger.error("Position {} not closed: {}", positionDTO.getPositionId(), orderCreationResult.getErrorMessage());
        }
        positionFlux.emitValue(positionDTO);
        return orderCreationResult;
    } else {
        logger.error("Impossible to close position {} because we couldn't find it in database", id);
        return new OrderCreationResultDTO("Impossible to close position " + id + " because we couldn't find it in database", null);
    }
}
Also used : Position(tech.cassandre.trading.bot.domain.Position) OrderCreationResultDTO(tech.cassandre.trading.bot.dto.trade.OrderCreationResultDTO) PositionDTO(tech.cassandre.trading.bot.dto.position.PositionDTO) BigDecimal(java.math.BigDecimal)

Example 3 with PositionDTO

use of tech.cassandre.trading.bot.dto.position.PositionDTO in project cassandre-trading-bot by cassandre-tech.

the class Issue852Test method baseAndQuotePrecisionManagementInPosition.

@Test
@DisplayName("Base and quote precisions are not saved in database")
public void baseAndQuotePrecisionManagementInPosition() {
    // Position 1 test (existing in database).
    final Optional<PositionDTO> position1 = positionService.getPositionByUid(1);
    assertTrue(position1.isPresent());
    assertEquals(new CurrencyDTO("SHIB"), position1.get().getCurrencyPair().getBaseCurrency());
    assertEquals(new CurrencyDTO("USDT"), position1.get().getCurrencyPair().getQuoteCurrency());
    assertEquals(1, position1.get().getCurrencyPair().getBaseCurrencyPrecision());
    assertEquals(2, position1.get().getCurrencyPair().getQuoteCurrencyPrecision());
    // Position 2 test (existing in database).
    final Optional<PositionDTO> position2 = positionService.getPositionByUid(2);
    assertTrue(position2.isPresent());
    assertEquals(new CurrencyDTO("ETH"), position2.get().getCurrencyPair().getBaseCurrency());
    assertEquals(new CurrencyDTO("BTC"), position2.get().getCurrencyPair().getQuoteCurrency());
    assertEquals(3, position2.get().getCurrencyPair().getBaseCurrencyPrecision());
    assertEquals(4, position2.get().getCurrencyPair().getQuoteCurrencyPrecision());
    // Position 3 test (manual creation).
    final CurrencyPairDTO newCurrencyPair = new CurrencyPairDTO("EUR", "SHIB", 6, 9);
    strategy.createLongPosition(newCurrencyPair, new BigDecimal("10"), PositionRulesDTO.builder().stopGainPercentage(// 1 000% max gain.
    1000f).stopLossPercentage(// 100% max lost.
    100f).build());
    final Optional<PositionDTO> position3 = positionService.getPositionByUid(3);
    assertTrue(position3.isPresent());
    assertEquals(new CurrencyDTO("EUR"), position3.get().getCurrencyPair().getBaseCurrency());
    assertEquals(new CurrencyDTO("SHIB"), position3.get().getCurrencyPair().getQuoteCurrency());
    assertEquals(6, position3.get().getCurrencyPair().getBaseCurrencyPrecision());
    assertEquals(9, position3.get().getCurrencyPair().getQuoteCurrencyPrecision());
}
Also used : CurrencyPairDTO(tech.cassandre.trading.bot.dto.util.CurrencyPairDTO) PositionDTO(tech.cassandre.trading.bot.dto.position.PositionDTO) CurrencyDTO(tech.cassandre.trading.bot.dto.util.CurrencyDTO) BigDecimal(java.math.BigDecimal) Test(org.junit.jupiter.api.Test) SpringBootTest(org.springframework.boot.test.context.SpringBootTest) DisplayName(org.junit.jupiter.api.DisplayName)

Example 4 with PositionDTO

use of tech.cassandre.trading.bot.dto.position.PositionDTO in project cassandre-trading-bot by cassandre-tech.

the class PositionServiceTest method checkOpeningOrderFailure.

@Test
@DisplayName("Check opening order failure")
public void checkOpeningOrderFailure() {
    // =============================================================================================================
    // Creates a position. Then send an order update with an error.
    // The position must end up being in OPENING_FAILURE
    // Creates position 1 (ETH/BTC, 0.0001, 10% stop gain).
    final PositionCreationResultDTO p1 = strategy.createLongPosition(ETH_BTC, new BigDecimal("0.0001"), PositionRulesDTO.builder().stopGainPercentage(10f).build());
    assertTrue(p1.isSuccessful());
    assertEquals(1, p1.getPosition().getUid());
    assertEquals("ORDER00010", p1.getPosition().getOpeningOrder().getOrderId());
    assertNull(p1.getErrorMessage());
    assertNull(p1.getException());
    assertTrue(positionService.getPositionByUid(1).isPresent());
    assertEquals(OPENING, positionService.getPositionByUid(1).get().getStatus());
    long position1Uid = p1.getPosition().getUid();
    // 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()));
    // Position 1.
    Optional<PositionDTO> position1 = strategy.getPositionByPositionId(position1Uid);
    assertTrue(position1.isPresent());
    // Opening order.
    OrderDTO p1OpeningOrder = position1.get().getOpeningOrder();
    assertNotNull(p1OpeningOrder);
    assertEquals("ORDER00010", p1OpeningOrder.getOrderId());
    assertEquals(BID, p1OpeningOrder.getType());
    assertEquals(ETH_BTC, p1OpeningOrder.getCurrencyPair());
    assertEquals(NEW, p1OpeningOrder.getStatus());
    // Closing order.
    OrderDTO p1ClosingOrder = position1.get().getClosingOrder();
    assertNull(p1ClosingOrder);
    // =============================================================================================================
    // An update for opening order ORDER00020 (position 2) arrives and change status with an error !
    OrderDTO order00010 = OrderDTO.builder().orderId("ORDER00010").type(BID).strategy(strategyDTO).amount(new CurrencyAmountDTO("0.00012", ETH_BTC.getBaseCurrency())).currencyPair(ETH_BTC).timestamp(ZonedDateTime.now()).status(STOPPED).build();
    orderFlux.emitValue(order00010);
    // The position should move to failure.
    await().untilAsserted(() -> assertEquals(OPENING_FAILURE, getPositionDTO(position1Uid).getStatus()));
    assertEquals("Position 1 - Opening failure", getPositionDTO(position1Uid).getDescription());
}
Also used : PositionCreationResultDTO(tech.cassandre.trading.bot.dto.position.PositionCreationResultDTO) CurrencyAmountDTO(tech.cassandre.trading.bot.dto.util.CurrencyAmountDTO) OrderDTO(tech.cassandre.trading.bot.dto.trade.OrderDTO) 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 5 with PositionDTO

use of tech.cassandre.trading.bot.dto.position.PositionDTO 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)

Aggregations

PositionDTO (tech.cassandre.trading.bot.dto.position.PositionDTO)22 BigDecimal (java.math.BigDecimal)19 DisplayName (org.junit.jupiter.api.DisplayName)16 Test (org.junit.jupiter.api.Test)16 PositionCreationResultDTO (tech.cassandre.trading.bot.dto.position.PositionCreationResultDTO)16 SpringBootTest (org.springframework.boot.test.context.SpringBootTest)15 CurrencyAmountDTO (tech.cassandre.trading.bot.dto.util.CurrencyAmountDTO)13 BaseTest (tech.cassandre.trading.bot.test.util.junit.BaseTest)12 Position (tech.cassandre.trading.bot.domain.Position)7 GainDTO (tech.cassandre.trading.bot.dto.util.GainDTO)7 OrderCreationResultDTO (tech.cassandre.trading.bot.dto.trade.OrderCreationResultDTO)6 OrderDTO (tech.cassandre.trading.bot.dto.trade.OrderDTO)6 TradeDTO (tech.cassandre.trading.bot.dto.trade.TradeDTO)6 Optional (java.util.Optional)5 CurrencyPairDTO (tech.cassandre.trading.bot.dto.util.CurrencyPairDTO)5 TickerDTO (tech.cassandre.trading.bot.dto.market.TickerDTO)4 LONG (tech.cassandre.trading.bot.dto.position.PositionTypeDTO.LONG)4 ZERO (java.math.BigDecimal.ZERO)3 Map (java.util.Map)3 Assertions.assertEquals (org.junit.jupiter.api.Assertions.assertEquals)3