Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 5 additions & 23 deletions backend/app/trade/service/trade_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "거래 가능한 활성화된 코인이 없습니다."
Expand All @@ -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(
Expand All @@ -113,9 +98,6 @@ async def execute(
)
continue

# 4. 거래 후 최종 잔고 기록
await self._record_balance()

return executed_trades

async def _process_coin_trade(
Expand Down
28 changes: 18 additions & 10 deletions backend/tests/app/trade/service/test_trade_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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

Expand All @@ -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(
Expand Down