From 738744813ce77ad2925f177805afbe95f24288ca Mon Sep 17 00:00:00 2001 From: ozgen Date: Fri, 26 Jan 2024 01:13:57 +0100 Subject: [PATCH] change open sellorder implementation --- .../manager/binance/BinanceApiManager.java | 7 +- .../binance/BinanceOpenSellOrderManager.java | 38 ++++++- .../repository/SellOrderRepository.java | 3 + ...BinanceNotCompletedSellOrderScheduler.java | 23 ++++ .../service/BotOrderService.java | 11 ++ .../BinanceOpenSellOrderManagerTest.java | 102 ++++++++++++++++++ .../repository/SellOrderRepositoryTest.java | 9 ++ ...nceNotCompletedSellOrderSchedulerTest.java | 34 ++++++ .../service/BotOrderServiceTest.java | 28 +++++ 9 files changed, 251 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/ozgen/telegrambinancebot/scheduling/binance/BinanceNotCompletedSellOrderScheduler.java create mode 100644 src/test/java/com/ozgen/telegrambinancebot/scheduling/binance/BinanceNotCompletedSellOrderSchedulerTest.java diff --git a/src/main/java/com/ozgen/telegrambinancebot/manager/binance/BinanceApiManager.java b/src/main/java/com/ozgen/telegrambinancebot/manager/binance/BinanceApiManager.java index 24e8f4a..aea0bbc 100644 --- a/src/main/java/com/ozgen/telegrambinancebot/manager/binance/BinanceApiManager.java +++ b/src/main/java/com/ozgen/telegrambinancebot/manager/binance/BinanceApiManager.java @@ -16,6 +16,7 @@ import org.springframework.stereotype.Component; import java.util.List; +import java.util.stream.Collectors; @Component @RequiredArgsConstructor @@ -50,9 +51,11 @@ public TickerData getTickerPrice24(String symbol) throws Exception { List getOpenOrders(String symbol) throws Exception { String openOrdersJson = this.binanceAPI.getOpenOrders(symbol); List orderInfoList = JsonParser.parseOrderInfoJson(openOrdersJson); - + List infoList = orderInfoList.stream() + .filter(orderInfo -> "BUY".equals(orderInfo.getSide())) + .collect(Collectors.toList()); log.info("'{}' of symbol open orders data are parsed, successfully.", symbol); - return this.binanceOrderService.createOrderInfos(orderInfoList); + return this.binanceOrderService.createOrderInfos(infoList); } List cancelOpenOrders(String symbol) throws Exception { diff --git a/src/main/java/com/ozgen/telegrambinancebot/manager/binance/BinanceOpenSellOrderManager.java b/src/main/java/com/ozgen/telegrambinancebot/manager/binance/BinanceOpenSellOrderManager.java index 4e8e9b4..79b9b85 100644 --- a/src/main/java/com/ozgen/telegrambinancebot/manager/binance/BinanceOpenSellOrderManager.java +++ b/src/main/java/com/ozgen/telegrambinancebot/manager/binance/BinanceOpenSellOrderManager.java @@ -3,6 +3,7 @@ import com.ozgen.telegrambinancebot.configuration.properties.ScheduleConfiguration; import com.ozgen.telegrambinancebot.model.ProcessStatus; import com.ozgen.telegrambinancebot.model.bot.BuyOrder; +import com.ozgen.telegrambinancebot.model.bot.SellOrder; import com.ozgen.telegrambinancebot.model.events.ErrorEvent; import com.ozgen.telegrambinancebot.model.events.NewSellOrderEvent; import com.ozgen.telegrambinancebot.model.telegram.TradingSignal; @@ -17,6 +18,7 @@ import java.util.Date; import java.util.List; +import java.util.stream.Collectors; @Component @RequiredArgsConstructor @@ -30,16 +32,48 @@ public class BinanceOpenSellOrderManager { public void processOpenSellOrders() { Date searchDate = getSearchDate(); - List tradingSignals = this.tradingSignalService.getTradingSignalsAfterDateAndIsProcessIn(searchDate, List.of(ProcessStatus.BUY)); - if (tradingSignals.isEmpty()){ + List tradingSignals = this.tradingSignalService + .getTradingSignalsAfterDateAndIsProcessIn(searchDate, List.of(ProcessStatus.BUY)); + if (tradingSignals.isEmpty()) { log.info("No trading signal has been detected."); return; } + List buyOrders = this.botOrderService.getBuyOrders(tradingSignals); buyOrders.forEach(this::safelyPublishNewSellOrder); } + public void processNotCompletedSellOrders() { + Date searchDate = getSearchDate(); + List sellSignals = this.tradingSignalService + .getTradingSignalsAfterDateAndIsProcessIn(searchDate, List.of(ProcessStatus.SELL)); + + if (sellSignals.isEmpty()) { + log.info("No trading signal has been detected."); + return; + } + List sellOrders = this.botOrderService.getSellOrders(sellSignals); + List buyOrders = this.botOrderService.getBuyOrders(sellSignals); + List matchingOrders = this.findMatchingOrders(sellOrders, buyOrders); + if (matchingOrders.isEmpty()) { + log.info("No matching has been detected."); + return; + } + matchingOrders.forEach(this::safelyPublishNewSellOrder); + } + + private List findMatchingOrders(List sellOrders, List buyOrders) { + return buyOrders.stream() + .filter(buyOrder -> { + TradingSignal buySignal = buyOrder.getTradingSignal(); + return sellOrders.stream() + .anyMatch(sellOrder -> sellOrder.getTradingSignal().getId().equals(buySignal.getId()) && + sellOrder.getCoinAmount() < buyOrder.getCoinAmount()); + }) + .collect(Collectors.toList()); + } + private void safelyPublishNewSellOrder(BuyOrder buyOrder) { try { this.publishNewSellOrder(buyOrder); diff --git a/src/main/java/com/ozgen/telegrambinancebot/repository/SellOrderRepository.java b/src/main/java/com/ozgen/telegrambinancebot/repository/SellOrderRepository.java index 5423585..014bba2 100644 --- a/src/main/java/com/ozgen/telegrambinancebot/repository/SellOrderRepository.java +++ b/src/main/java/com/ozgen/telegrambinancebot/repository/SellOrderRepository.java @@ -5,11 +5,14 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.List; import java.util.Optional; @Repository public interface SellOrderRepository extends JpaRepository { Optional findByTradingSignal(TradingSignal tradingSignal); + List findByTradingSignalIn(List tradingSignals); + } diff --git a/src/main/java/com/ozgen/telegrambinancebot/scheduling/binance/BinanceNotCompletedSellOrderScheduler.java b/src/main/java/com/ozgen/telegrambinancebot/scheduling/binance/BinanceNotCompletedSellOrderScheduler.java new file mode 100644 index 0000000..92664a9 --- /dev/null +++ b/src/main/java/com/ozgen/telegrambinancebot/scheduling/binance/BinanceNotCompletedSellOrderScheduler.java @@ -0,0 +1,23 @@ +package com.ozgen.telegrambinancebot.scheduling.binance; + +import com.ozgen.telegrambinancebot.manager.binance.BinanceOpenSellOrderManager; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@Slf4j +public class BinanceNotCompletedSellOrderScheduler { + + private final BinanceOpenSellOrderManager binanceOpenSellOrderManager; + + + @Scheduled(fixedRateString = "#{${app.bot.schedule.openSellOrder}}") + public void processNotCompletedSellOrders() { + log.info("NotCompletedSellOrderScheduler has been started"); + this.binanceOpenSellOrderManager.processNotCompletedSellOrders(); + log.info("NotCompletedSellOrderScheduler has been finished"); + } +} diff --git a/src/main/java/com/ozgen/telegrambinancebot/service/BotOrderService.java b/src/main/java/com/ozgen/telegrambinancebot/service/BotOrderService.java index 4efa2c4..b745a27 100644 --- a/src/main/java/com/ozgen/telegrambinancebot/service/BotOrderService.java +++ b/src/main/java/com/ozgen/telegrambinancebot/service/BotOrderService.java @@ -84,4 +84,15 @@ public List getBuyOrders(List tradingSignals) { return List.of(); // Return an empty list in case of error } } + + public List getSellOrders(List tradingSignals) { + try { + List sellOrders = sellOrderRepository.findByTradingSignalIn(tradingSignals); + log.info("Retrieved {} sell orders for trading signals", sellOrders.size()); + return sellOrders; + } catch (Exception e) { + log.error("Error retrieving buy orders: {}", e.getMessage(), e); + return List.of(); // Return an empty list in case of error + } + } } diff --git a/src/test/java/com/ozgen/telegrambinancebot/manager/binance/BinanceOpenSellOrderManagerTest.java b/src/test/java/com/ozgen/telegrambinancebot/manager/binance/BinanceOpenSellOrderManagerTest.java index 50fa97c..a4515bc 100644 --- a/src/test/java/com/ozgen/telegrambinancebot/manager/binance/BinanceOpenSellOrderManagerTest.java +++ b/src/test/java/com/ozgen/telegrambinancebot/manager/binance/BinanceOpenSellOrderManagerTest.java @@ -4,6 +4,7 @@ import com.ozgen.telegrambinancebot.configuration.properties.ScheduleConfiguration; import com.ozgen.telegrambinancebot.model.ProcessStatus; import com.ozgen.telegrambinancebot.model.bot.BuyOrder; +import com.ozgen.telegrambinancebot.model.bot.SellOrder; import com.ozgen.telegrambinancebot.model.events.NewSellOrderEvent; import com.ozgen.telegrambinancebot.model.telegram.TradingSignal; import com.ozgen.telegrambinancebot.service.BotOrderService; @@ -33,11 +34,14 @@ public class BinanceOpenSellOrderManagerTest { private static final String SYMBOL = "BNBBTC"; private static final Double BUY_PRICE = 0.01; + private static final Double SELL_PRICE = 0.011; private static final String START_ENTRY = "0.009"; private static final String END_ENTRY = "0.011"; private static final String STOPLOSS = "0.008"; private static final Double BNB_TOTAL_AMOUNT = 0.23; + private static final Double BNB_LESS_AMOUNT = 0.13; + private static final String SIGNAL_ID = "signal_id"; @Mock private TradingSignalService tradingSignalService; @@ -56,10 +60,14 @@ public class BinanceOpenSellOrderManagerTest { private BuyOrder buyOrder; + private SellOrder sellOrder; + @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); + when(this.tradingSignal.getId()) + .thenReturn(SIGNAL_ID); when(this.tradingSignal.getSymbol()) .thenReturn(SYMBOL); when(this.tradingSignal.getEntryStart()) @@ -80,6 +88,13 @@ void setUp() { this.buyOrder.setCoinAmount(BNB_TOTAL_AMOUNT); this.buyOrder.setStopLoss(0.012); this.buyOrder.setTradingSignal(this.tradingSignal); + + this.sellOrder = new SellOrder(); + this.sellOrder.setSymbol(SYMBOL); + this.sellOrder.setSellPrice(SELL_PRICE); + this.sellOrder.setCoinAmount(BNB_LESS_AMOUNT); + this.sellOrder.setStopLoss(0.012); + this.sellOrder.setTradingSignal(this.tradingSignal); } @Test @@ -173,4 +188,91 @@ public void testProcessOpenSellOrders_withException() { mockedSyncUtil.close(); } } + + @Test + public void testProcessNotCompletedSellOrders_Success() { + // Arrange + when(this.tradingSignalService.getTradingSignalsAfterDateAndIsProcessIn(any(), argThat(list -> list.contains(ProcessStatus.SELL)))) + .thenReturn(List.of(this.tradingSignal)); + when(this.botOrderService.getSellOrders(argThat(list -> list.contains(this.tradingSignal)))) + .thenReturn(List.of(this.sellOrder)); + when(this.botOrderService.getBuyOrders(argThat(list -> list.contains(this.tradingSignal)))) + .thenReturn(List.of(this.buyOrder)); + + // Act + this.binanceOpenSellOrderManager.processNotCompletedSellOrders(); + + // Assert + verify(this.tradingSignalService) + .getTradingSignalsAfterDateAndIsProcessIn(any(), argThat(list -> list.contains(ProcessStatus.SELL))); + verify(this.botOrderService) + .getBuyOrders(argThat(list -> list.contains(this.tradingSignal))); + ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(NewSellOrderEvent.class); + verify(this.publisher) + .publishEvent(eventCaptor.capture()); + NewSellOrderEvent newSellOrderEvent = eventCaptor.getValue(); + assertEquals(this.buyOrder, newSellOrderEvent.getBuyOrder()); + } + + @Test + public void testProcessNotCompletedSellOrders_withEmptyBuyOrders() { + // Arrange + when(this.tradingSignalService.getTradingSignalsAfterDateAndIsProcessIn(any(), argThat(list -> list.contains(ProcessStatus.SELL)))) + .thenReturn(List.of(this.tradingSignal)); + when(this.botOrderService.getSellOrders(argThat(list -> list.contains(this.tradingSignal)))) + .thenReturn(List.of(this.sellOrder)); + when(this.botOrderService.getBuyOrders(argThat(list -> list.contains(this.tradingSignal)))) + .thenReturn(List.of()); + + // Act + this.binanceOpenSellOrderManager.processNotCompletedSellOrders(); + + // Assert + verify(this.tradingSignalService) + .getTradingSignalsAfterDateAndIsProcessIn(any(), argThat(list -> list.contains(ProcessStatus.SELL))); + verify(this.botOrderService) + .getBuyOrders(argThat(list -> list.contains(this.tradingSignal))); + verify(this.publisher, never()) + .publishEvent(any()); + } + + @Test + public void testProcessNotCompletedSellOrders_withEmptySellOrders() { + // Arrange + when(this.tradingSignalService.getTradingSignalsAfterDateAndIsProcessIn(any(), argThat(list -> list.contains(ProcessStatus.SELL)))) + .thenReturn(List.of(this.tradingSignal)); + when(this.botOrderService.getSellOrders(argThat(list -> list.contains(this.tradingSignal)))) + .thenReturn(List.of()); + when(this.botOrderService.getBuyOrders(argThat(list -> list.contains(this.tradingSignal)))) + .thenReturn(List.of(this.buyOrder)); + + // Act + this.binanceOpenSellOrderManager.processNotCompletedSellOrders(); + + // Assert + verify(this.tradingSignalService) + .getTradingSignalsAfterDateAndIsProcessIn(any(), argThat(list -> list.contains(ProcessStatus.SELL))); + verify(this.botOrderService) + .getBuyOrders(argThat(list -> list.contains(this.tradingSignal))); + verify(this.publisher, never()) + .publishEvent(any()); + } + + @Test + public void testProcessNotCompletedSellOrders_withEmptyTradingSignalList() { + // Arrange + when(this.tradingSignalService.getTradingSignalsAfterDateAndIsProcessIn(any(), argThat(list -> list.contains(ProcessStatus.SELL)))) + .thenReturn(List.of()); + + // Act + this.binanceOpenSellOrderManager.processNotCompletedSellOrders(); + + // Assert + verify(this.tradingSignalService) + .getTradingSignalsAfterDateAndIsProcessIn(any(), argThat(list -> list.contains(ProcessStatus.SELL))); + verify(this.botOrderService, never()) + .getBuyOrders(any()); + verify(this.publisher, never()) + .publishEvent(any()); + } } diff --git a/src/test/java/com/ozgen/telegrambinancebot/repository/SellOrderRepositoryTest.java b/src/test/java/com/ozgen/telegrambinancebot/repository/SellOrderRepositoryTest.java index 7d7cc18..3078d45 100644 --- a/src/test/java/com/ozgen/telegrambinancebot/repository/SellOrderRepositoryTest.java +++ b/src/test/java/com/ozgen/telegrambinancebot/repository/SellOrderRepositoryTest.java @@ -10,9 +10,11 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; import org.springframework.test.context.TestPropertySource; +import java.util.List; import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @DataJpaTest @@ -44,5 +46,12 @@ public void testFindByTradingSignal() { assertTrue(foundOrder.isPresent()); assertEquals(this.sellOrder, foundOrder.get()); } + + @Test + public void testFindByTradingSignalIn() { + List foundOrder = this.sellOrderRepository.findByTradingSignalIn(List.of(this.tradingSignal)); + assertFalse(foundOrder.isEmpty()); + assertEquals(1, foundOrder.size()); + } } diff --git a/src/test/java/com/ozgen/telegrambinancebot/scheduling/binance/BinanceNotCompletedSellOrderSchedulerTest.java b/src/test/java/com/ozgen/telegrambinancebot/scheduling/binance/BinanceNotCompletedSellOrderSchedulerTest.java new file mode 100644 index 0000000..c1049fe --- /dev/null +++ b/src/test/java/com/ozgen/telegrambinancebot/scheduling/binance/BinanceNotCompletedSellOrderSchedulerTest.java @@ -0,0 +1,34 @@ +package com.ozgen.telegrambinancebot.scheduling.binance; + +import com.ozgen.telegrambinancebot.manager.binance.BinanceOpenSellOrderManager; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.mockito.Mockito.verify; + + +public class BinanceNotCompletedSellOrderSchedulerTest { + + @Mock + private BinanceOpenSellOrderManager binanceOpenSellOrderManager; + + @InjectMocks + private BinanceNotCompletedSellOrderScheduler binanceNotCompletedSellOrderScheduler; + + @BeforeEach + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testProcessOpenSellOrders() { + this.binanceNotCompletedSellOrderScheduler.processNotCompletedSellOrders(); + + verify(this.binanceOpenSellOrderManager) + .processNotCompletedSellOrders(); + } + +} diff --git a/src/test/java/com/ozgen/telegrambinancebot/service/BotOrderServiceTest.java b/src/test/java/com/ozgen/telegrambinancebot/service/BotOrderServiceTest.java index 9dca290..ff114b2 100644 --- a/src/test/java/com/ozgen/telegrambinancebot/service/BotOrderServiceTest.java +++ b/src/test/java/com/ozgen/telegrambinancebot/service/BotOrderServiceTest.java @@ -213,4 +213,32 @@ public void testGetBuyOrders_Exception() { verify(buyOrderRepository) .findByTradingSignalIn(signals); } + + @Test + public void testGetSellOrders_Success() { + List signals = List.of(new TradingSignal()); + List expectedOrders = List.of(new SellOrder()); + when(this.sellOrderRepository.findByTradingSignalIn(signals)) + .thenReturn(expectedOrders); + + List result = this.botOrderService.getSellOrders(signals); + + assertNotNull(result); + assertEquals(expectedOrders, result); + verify(this.sellOrderRepository) + .findByTradingSignalIn(signals); + } + + @Test + public void testGetSellOrders_Exception() { + List signals = List.of(new TradingSignal()); + when(this.sellOrderRepository.findByTradingSignalIn(signals)) + .thenThrow(new RuntimeException("Test exception")); + + List result = this.botOrderService.getSellOrders(signals); + + assertTrue(result.isEmpty()); + verify(this.sellOrderRepository) + .findByTradingSignalIn(signals); + } }