diff --git a/backend/app/trade/service/trade_service.py b/backend/app/trade/service/trade_service.py index 7745bcf..ec38f2a 100644 --- a/backend/app/trade/service/trade_service.py +++ b/backend/app/trade/service/trade_service.py @@ -48,6 +48,9 @@ async def execute( # 1. 활성화된 모든 코인 조회 active_coins = await self.coin_service.get_all_active() + + # 2. 거래 전 잔고 기록 + await self._record_balance() if not active_coins: reason = "거래 가능한 활성화된 코인이 없습니다." @@ -67,28 +70,10 @@ async def execute( executed_trades.append(no_action_trade) return executed_trades - # 2. 현재 KRW 잔고 조회 + # 3. 현재 KRW 잔고 조회 krw_balance = self.upbit_client.get_krw_balance() - if krw_balance == 0: - reason = f"KRW 잔고가 없습니다. 매수 불가 (잔고: {krw_balance:,.0f}원)" - - # NO_ACTION 상태로 기록 - trade = Trade( - coin_id=None, - trade_type=None, - price=Decimal("0"), - amount=Decimal("0"), - risk_level=RiskLevel.NONE.value, - status=TradeStatus.NO_ACTION, - ai_reason=None, - execution_reason=reason, - ) - no_action_trade = await self.trade_repository.create(trade) - executed_trades.append(no_action_trade) - return executed_trades - - # 3. 각 코인에 대해 AI 분석 및 거래 실행 + # 4. 각 코인에 대해 AI 분석 및 거래 실행 for coin in active_coins: try: coin_trade: Optional[Trade] = await self._process_coin_trade( @@ -113,9 +98,6 @@ async def execute( ) continue - # 4. 거래 후 최종 잔고 기록 - await self._record_balance() - return executed_trades async def _process_coin_trade( diff --git a/backend/tests/app/trade/service/test_trade_service.py b/backend/tests/app/trade/service/test_trade_service.py index 20321c3..4ec64a6 100644 --- a/backend/tests/app/trade/service/test_trade_service.py +++ b/backend/tests/app/trade/service/test_trade_service.py @@ -23,11 +23,12 @@ class TestExecute: """execute() 메서드 테스트""" async def test_execute_no_active_coins( - self, trade_service, mock_coin_service, mock_trade_repository + self, trade_service, mock_coin_service, mock_trade_repository, mock_upbit_client, mock_balance_repository ): """활성 코인이 없는 경우 NO_ACTION 상태로 기록""" # Given: 활성 코인이 없음 mock_coin_service.get_all_active.return_value = [] + mock_upbit_client.get_krw_balance.return_value = 100000 mock_trade = Trade( coin_id=None, @@ -56,22 +57,29 @@ async def test_execute_zero_krw_balance( mock_coin_service, mock_upbit_client, mock_trade_repository, + mock_balance_repository, + mock_ai_client, sample_coin, + sample_ai_result_buy, ): - """KRW 잔고가 0인 경우 NO_ACTION 상태로 기록""" - # Given: 활성 코인은 있지만 KRW 잔고가 0 + """KRW 잔고가 0인 경우 AI가 BUY 결정해도 NO_ACTION 상태로 기록""" + # Given: 활성 코인은 있지만 KRW 잔고가 0, AI는 BUY 결정 mock_coin_service.get_all_active.return_value = [sample_coin] mock_upbit_client.get_krw_balance.return_value = 0 + mock_upbit_client.get_coin_balance.return_value = 0 + mock_upbit_client.get_ohlcv_raw.return_value = MagicMock() + mock_upbit_client.get_current_price.return_value = 50000000 + mock_ai_client.get_bitcoin_trading_decision.return_value = sample_ai_result_buy mock_trade = Trade( - coin_id=None, - trade_type=None, - price=Decimal("0"), + coin_id=sample_coin.id, + trade_type=TradeType.BUY.value, + price=Decimal("50000000"), amount=Decimal("0"), - risk_level=RiskLevel.NONE.value, + risk_level=sample_ai_result_buy.risk_level.value, status=TradeStatus.NO_ACTION, - ai_reason=None, - execution_reason="KRW 잔고가 없습니다. 매수 불가 (잔고: 0원)", + ai_reason=sample_ai_result_buy.reason, + execution_reason="KRW 잔고가 5000원 미만입니다. 매수 불가 (가용 금액: 0원)", ) mock_trade_repository.create.return_value = mock_trade @@ -81,7 +89,7 @@ async def test_execute_zero_krw_balance( # Then: NO_ACTION 거래 기록 생성 assert len(result) == 1 assert result[0].status == TradeStatus.NO_ACTION - assert "KRW 잔고가 없습니다" in result[0].execution_reason + assert "매수 불가" in result[0].execution_reason mock_trade_repository.create.assert_called_once() async def test_execute_success_with_buy(