From a466edc1c26769d97f06740fb1e40d510692d26c Mon Sep 17 00:00:00 2001 From: rookedsysc Date: Sat, 22 Nov 2025 22:45:08 +0900 Subject: [PATCH 1/2] =?UTF-8?q?modify:=20=EC=9E=94=EA=B3=A0=20=ED=95=AD?= =?UTF-8?q?=EC=83=81=20=EA=B8=B0=EB=A1=9D=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=20krw=200=EC=9B=90=EC=9D=BC=20=EB=95=8C?= =?UTF-8?q?=20=EC=96=B4=EC=B0=A8=ED=94=BC=20=EB=B0=91=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=ED=95=A8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/trade/service/trade_service.py | 28 ++++------------------ 1 file changed, 5 insertions(+), 23 deletions(-) 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( From 131eb6ec061ec671953cdb3e5bc11fdc374dd8ea Mon Sep 17 00:00:00 2001 From: rookedsysc Date: Sat, 22 Nov 2025 22:45:38 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix(test):=20=5Frecord=5Fbalance=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=EC=97=90=20=ED=95=84=EC=9A=94=ED=95=9C=20moc?= =?UTF-8?q?k=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - test_execute_no_active_coins: mock_upbit_client 추가하여 get_krw_balance 설정 - test_execute_zero_krw_balance: 비즈니스 로직에 맞게 AI 분석 mock 추가 및 시나리오 수정 --- .../app/trade/service/test_trade_service.py | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) 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(