Files
AutoCoinTrader/docs/upbit_api_review.md
2025-12-09 21:39:23 +09:00

8.5 KiB

Upbit API 사용법 검토 보고서

검토 일시: 2025-12-04 검토 범위: order.py, holdings.py의 Upbit API 호출 결론: 대부분 올바름, 1개 잠재적 이슈 확인


📋 검토 항목

1. 주문 API 사용법

1.1 시장가 매수 (buy_market_order)

현재 코드 (order.py):

resp = upbit.buy_market_order(market, amount_krw)

Upbit API 스펙:

  • 함수명: buy_market_order(ticker, price)
  • ticker: 마켓 심볼 (예: "KRW-BTC")
  • price: 매수할 KRW 금액 (예: 15000)

검증: 올바름

  • market = "KRW-BTC" ✓
  • amount_krw = 원화 금액 ✓

1.2 지정가 매수 (buy_limit_order)

현재 코드 (order.py):

volume = amount_krw / adjusted_limit_price
resp = upbit.buy_limit_order(market, adjusted_limit_price, volume)

Upbit API 스펙:

  • 함수명: buy_limit_order(ticker, price, volume)
  • ticker: 마켓 심볼
  • price: 지정가 (KRW 단위) - 예: 50000000
  • volume: 매수 수량 (개수) - 예: 0.001

검증: 올바름

  • price = 조정된 호가 ✓
  • volume = KRW / 가격 = 개수 ✓
  • 호가 단위 조정 포함 ✓

주의: 호가 단위 조정 (adjust_price_to_tick_size)는 좋은 실천


1.3 시장가 매도 (sell_market_order)

현재 코드 (order.py):

resp = upbit.sell_market_order(market, amount)

Upbit API 스펙:

  • 함수명: sell_market_order(ticker, volume)
  • ticker: 마켓 심볼
  • volume: 매도 수량 (개수, NOT KRW)

검증: 올바름

  • market 유효함 ✓
  • amount는 개수 단위

⚠️ 중요 주석 확인:

# pyupbit API: sell_market_order(ticker, volume)
#   - ticker: 마켓 코드 (예: "KRW-BTC")
#   - volume: 매도할 코인 수량 (개수, not KRW)
# 잘못된 사용 예시: sell_market_order("KRW-BTC", 500000) → BTC 500,000개 매도 시도! ❌
# 올바른 사용 예시: sell_market_order("KRW-BTC", 0.01) → BTC 0.01개 매도 ✅

코드 자체는 올바르지만, 충분한 안전장치가 있습니다.


2. 잔고 조회 API (get_balances)

현재 코드 (holdings.py):

balances = upbit.get_balances()
# 응답: List[dict]
for item in balances:
    currency = item.get("currency")
    balance = float(item.get("balance", 0))

Upbit API 스펙:

  • 함수명: get_balances()
  • 반환값: 리스트 of 딕셔너리
[
  {
    "currency": "BTC",
    "balance": "0.5",
    "locked": "0.0",
    "avg_buy_price": "50000000",
    "avg_buy_price_krw": "50000000"
  }
]

검증: 올바름

  • 리스트 타입 확인 ✓
  • currency 필드 접근 ✓
  • balance 문자열 → float 변환 ✓
  • 금액 단위 명확함 ✓

3. 주문 상태 조회 (get_order)

현재 코드 (order.py):

order = cb.call(upbit.get_order, current_uuid)
state = order.get("state")
volume = float(order.get("volume", 0))
executed = float(order.get("executed_volume", 0) or order.get("filled_volume", 0))

Upbit API 스펙:

  • 함수명: get_order(uuid)
  • 반환값: dict
{
  "uuid": "9ca023f5-...",
  "side": "bid",
  "ord_type": "limit",
  "price": "50000000",
  "state": "done",
  "market": "KRW-BTC",
  "created_at": "2021-01-01T00:00:00+00:00",
  "volume": "0.1",
  "remaining_volume": "0.05",
  "reserved_fee": "50000",
  "remaining_fee": "0",
  "paid_fee": "50000",
  "locked": "5000000",
  "executed_volume": "0.05",
  "trades_count": 2,
  "trades": [...]
}

검증: 올바름

  • uuid 파라미터 올바름 ✓
  • state 필드 확인 ✓
  • volume 수량 단위 (BTC 개수) ✓
  • executed_volume vs filled_volume: 잠재적 이슈 ⚠️

⚠️ 주의점 - filled_volume 필드:

executed = float(order.get("executed_volume", 0) or order.get("filled_volume", 0))

현재 코드는 fallback을 시도하지만:

  • Upbit API는 executed_volume 필드만 사용
  • filled_volume은 다른 거래소 필드명 (바이낸스 등)
  • 현재 코드는 작동하지만 불필요한 fallback

개선 제안:

executed = float(order.get("executed_volume", 0) or 0.0)

4. 현재가 조회 (get_current_price)

현재 코드 (holdings.py):

price = pyupbit.get_current_price(market)
return float(price) if price else 0.0

Upbit API 스펙:

  • 함수명: get_current_price(ticker)
  • 반환값: int (원화 단위)
  • 예: 50000000 (50 백만 원)

검증: 올바름

  • 마켓 심볼 올바름 ✓
  • int → float 변환 ✓
  • Null 처리 ✓

5. 주문 취소 (cancel_order)

현재 코드 (order.py):

cancel_resp = cb.call(upbit.cancel_order, current_uuid)

Upbit API 스펙:

  • 함수명: cancel_order(uuid)
  • 반환값: dict (취소된 주문 정보)

검증: 올바름

  • uuid 파라미터 ✓
  • 반환값은 주문 상태 dict ✓

6. 호가 단위 조정 (get_tick_size)

현재 코드 (order.py):

tick_size = pyupbit.get_tick_size(price)
adjusted_price = round(price / tick_size) * tick_size

Upbit API 스펙:

  • 함수명: get_tick_size(price)
  • 반환값: float (호가 단위)
  • 예: price=50000000 → tick_size=100

검증: 올바름

  • 가격을 호가 단위로 정규화 ✓
  • 올바른 반올림 논리 ✓
  • API 오류 시 원본 가격 반환 ✓

우수 실천: 호가 단위 조정으로 주문 거부 사전 방지


🔍 구체적 검토 결과

올바른 사항

API 함수 파라미터 반환값 상태
buy_market_order (ticker, price_krw) dict 올바름
buy_limit_order (ticker, price, volume) dict 올바름
sell_market_order (ticker, volume) dict 올바름
get_balances () list[dict] 올바름
get_order (uuid) dict 올바름
cancel_order (uuid) dict 올바름
get_current_price (ticker) int 올바름
get_tick_size (price) float 올바름

잠재적 이슈 ⚠️

이슈 1: filled_volume 필드 오류 (상태: 저위험)

위치: order.py line ~820

executed = float(order.get("executed_volume", 0) or order.get("filled_volume", 0))

문제:

  • filled_volume은 Upbit API에 없는 필드
  • fallback이 항상 실패해도 안전 (0 또는 executed_volume 사용)
  • 하지만 불필요한 fallback

영향도: 낮음 (현재 코드는 작동함)

개선:

executed = float(order.get("executed_volume", 0.0))

🎯 권장사항

즉시 적용 (Priority: Medium)

1. filled_volume 필드 제거

order.py line ~820 수정:

# Before
executed = float(order.get("executed_volume", 0) or order.get("filled_volume", 0))

# After
executed = float(order.get("executed_volume", 0.0))

선택사항 (Priority: Low)

1. API 응답 필드명 명시 주석 추가

각 API 함수 호출 전에 반환값 필드명 추가:

# pyupbit.get_order() 반환 필드: uuid, state, side, market, volume, executed_volume, trades[]
order = upbit.get_order(current_uuid)

📊 현재 코드 평가

강점

모든 API 파라미터 사용법 올바름 응답 데이터 타입 검증 완료 Null/예외 처리 포함 호가 단위 조정으로 추가 안정성 확보 Circuit Breaker로 API 실패 격리

약점

⚠️ 불필요한 fallback 필드 (filled_volume) ⚠️ API 응답 필드명 문서화 부족

종합 평가

신뢰도: 95/100 - 실무 운영 가능 수준


🔗 Upbit API 공식 문서 참고

REST API 사용 가이드

호가 정책 (Tick Size)

에러 처리


결론

현재 코드의 Upbit API 사용법은 기본적으로 올바릅니다.

  • 모든 주요 API 함수 파라미터 정확함
  • 응답 데이터 파싱 올바름
  • 타입 변환 적절함
  • ⚠️ 미미한 불필요한 fallback 존재

권장: filled_volume 필드 참조 제거 후 프로덕션 배포 가능


검토자: GitHub Copilot (Claude Haiku 4.5) 검토 기준: Upbit API 공식 문서 마지막 업데이트: 2025-12-04