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());
}
}
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);
}
}
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());
}
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());
}
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());
}
Aggregations