업비트 원화 마켓 24시간 자동매매 가이드
업데이트: 2026-10-10
국내 1위 가상화폐 거래소인 업비트는 안정적인 원화 입출금과 풍부한 유동성을 제공하지만, 알고리즘 자동매매 봇 개발자에게는 매우 엄격한 API 제약 조건을 요구합니다.
주문 요청이 몰리는 급변동장에서 초당 12회 제한을 초과해 거래소로부터 일시 차단(429 밴)당하거나, 멀티스레드 환경에서 주문 서명이 충돌(Invalid nonce)하여 긴급 손절 기회를 놓치는 사고를 완벽히 차단하는 프로덕션 엔지니어링 패턴을 상세히 다룹니다.
업비트 OpenAPI 호출 제한 규정
업비트 서버는 요청 종류에 따라 독립된 초당 쿼터를 엄격하게 카운트합니다. 2026년 8월 21일부터 주문 한도가 초당 8회에서 12회로 상향되었고, 분 단위 제한(min)은 폐지되었습니다.
| API 그룹 | 대상 엔드포인트 | 초당 최대 한도 | 적용 단위 | 초과 시 결과 |
|---|---|---|---|---|
| 주문 (order) | 주문 생성, 취소 후 재주문 | 초당 최대 12회 | 포켓 | 즉시 429 Too Many Requests 반환 및 차단 |
| 자산·주문 관리 (default) | 잔고 조회, 주문 조회, 개별 주문 취소 | 초당 최대 30회 | 포켓 | 일시적 요청 거절 |
| 시세 조회 (Quotation) | 페어·캔들·체결·현재가·호가 조회 | 그룹별 초당 최대 10회 | IP | IP 단위 일시 차단 |
| 주문 일괄 취소 | 일괄 취소 API | 2초당 최대 1회 | 포켓 | 일시적 요청 거절 |
업비트 공식 제한은 초당 12회이지만, 네트워크 전송 지연으로 패킷이 몰려 도착할 수 있으므로, 프로덕션 봇에서는 발송 큐의 상한선을 **초당 10회(간격 약 0.1초)**로 설정하여 절대 한도를 넘지 않도록 안전 마진을 두어야 합니다.
동시 주문 시 서명 충돌(Invalid nonce) 방어 원리
업비트 REST API 인증은 JWT(JSON Web Token) 페이로드 내에 매 요청마다 고유한 nonce 값을 포함해야 합니다.
여러 코인을 동시에 스캔하는 봇에서 코루틴들이 동시에 주문 서명을 생성하면, 밀리초 타임스탬프가 겹쳐 동일한 nonce가 생성되고 업비트 서버는 {"error":{"name":"invalid_nonce","message":"Invalid nonce"}} 에러와 함께 주문을 단칼에 거절합니다.
[ 취약한 동시 요청 (충돌 발생) ]
코루틴 A (BTC 매수) ──► nonce="1710000000100" ──┐
코루틴 B (ETH 매수) ──► nonce="1710000000100" ──┼──► 업비트 서버: "동일 Nonce 충돌! 동일 Nonce 충돌로 주문 거절" ❌
[ DashCloud 순차 큐잉 (안전) ]
코루틴 A, B 주문 생성 ──► [ 단일 비동기 주문 큐 ] ──► UUID v4 기반 고유 Nonce 생성 ──► 초당 10회 순차 집행 (100% 성공) ✅완성형 업비트 쿼터 준수 주문 엔진 (upbit_client.py)
초당 10회 속도 제어(12회 한도 대비 안전 마진), UUID 기반 Nonce 생성, 잔여 쿼터 헤더(Remaining-Req) 자동 분석이 결합된 완성형 모듈입니다.
import os
import time
import uuid
import hashlib
import jwt
import requests
import asyncio
import logging
from urllib.parse import urlencode, unquote
logger = logging.getLogger("UpbitClient")
class UpbitSafeClient:
def __init__(self, access_key: str, secret_key: str):
self.access_key = access_key
self.secret_key = secret_key
self.server_url = "https://api.upbit.com"
self.last_order_time = 0.0
self.min_order_interval = 0.1 # 초당 약 10회로 제한 (12회 한도 준수)
def _generate_auth_headers(self, query_params: dict = None):
"""UUID v4 기반 무충돌 Nonce JWT 인증 토큰 생성"""
payload = {
"access_key": self.access_key,
"nonce": str(uuid.uuid4()), # 타임스탬프 대신 UUID를 사용하여 충돌 원천 차단
}
if query_params:
query_string = unquote(urlencode(query_params, doseq=True)).encode("utf-8")
m = hashlib.sha512()
m.update(query_string)
query_hash = m.hexdigest()
payload["query_hash"] = query_hash
payload["query_hash_alg"] = "SHA512"
jwt_token = jwt.encode(payload, self.secret_key, algorithm="HS512")
return {"Authorization": f"Bearer {jwt_token}"}
async def create_market_buy_order(self, market: str, price_krw: int):
"""
원화 시장가 매수 (초당 12회 쿼터 준수 딜레이 적용)
:param market: 마켓 코드 (예: "KRW-BTC")
:param price_krw: 매수할 총 원화 금액 (최소 5,000원 이상)
"""
if price_krw < 5000:
raise ValueError("업비트 최소 주문 금액은 5,000원 이상이어야 합니다.")
# 쿼터 준수를 위한 대기 시간 제어
elapsed = time.time() - self.last_order_time
if elapsed < self.min_order_interval:
await asyncio.sleep(self.min_order_interval - elapsed)
params = {
"market": market,
"side": "bid", # 매수
"price": str(price_krw),
"ord_type": "price", # 시장가 매수
}
headers = self._generate_auth_headers(params)
try:
res = requests.post(f"{self.server_url}/v1/orders", json=params, headers=headers, timeout=5)
self.last_order_time = time.time()
# 업비트 잔여 쿼터 헤더 파싱 로깅
remaining = res.headers.get("Remaining-Req", "")
logger.debug(f"주문 응답 완료. 잔여 쿼터: {remaining}")
if res.status_code == 201:
order_data = res.json()
logger.info(f"✅ 매수 성공 [{market}]: 주문 ID {order_data['uuid']}, 금액: {price_krw:,}원")
return order_data
else:
logger.error(f"❌ 매수 실패 [{market}] ({res.status_code}): {res.text}")
return None
except Exception as e:
logger.error(f"주문 통신 중 오류: {e}")
return None미체결 고아 주문 방지: 주문 생명주기 상태 머신
네트워크 단절이나 일시적 타임아웃으로 인해 주문이 거래소에 접수되었는지 불확실할 때 무작정 재주문을 넣으면 의도치 않은 2배 매수가 발생합니다.
주문이 생성되면 반드시 주문 상태 머신을 통해 상태를 추적해야 합니다.
[ create_order 지정가 주문 접수 ] ──► 주문 UUID 로컬 기록
│
▼
[ 30초 타이머 가동 ]
│
┌───────┴────────────────────────┐
(체결 완료) (미체결 잔존)
│ │
▼ ▼
[정상 익절 루프] ┌─────────────────────────────────┐
│ 1. cancel_order(UUID) 취소 요청 │
│ 2. 취소 성공 확인 후 호가 재조정 │
└─────────────────────────────────┘업비트 API Key 발급 4단계
업비트는 자산 보호를 위해 API Key 생성 시 최대 4개의 접속 허용 IP를 등록하도록 요구하고 있습니다.
허용 IP 준비
업비트는 API Key 생성 시 허용 IP 등록이 필요합니다. DashCloud는 프로젝트별 고정 아웃바운드 IP를 대시보드에 표시하는 기능을 아직 제공하지 않으므로, IP 화이트리스트 연동이 필요하면 [email protected]로 문의해 주세요.
업비트 Open API 관리 페이지 접속
업비트 웹사이트에 로그인한 후, 우측 상단 마이페이지 ➔ Open API 관리 메뉴로 이동합니다.
허용 IP 주소 입력 및 권한 설정
- 허용 IP 주소: 업비트에 등록할 접속 허용 IP를 입력합니다(위 안내 참고).
- 접근 권한 체크:
자산조회: ✅ 체크주문조회: ✅ 체크주문하기: ✅ 체크출금하기: ❌ 절대 체크 금지 (자산 보호 필수)
카카오페이 / 네이버 2차 인증 완료
스마트폰 인증을 마치면 새로운 Access Key와 Secret Key가 발급됩니다. 대시보드 환경변수(UPBIT_ACCESS_KEY, UPBIT_SECRET_KEY)에 즉시 등록하세요.