파이썬으로 업비트 API 연동하기 — 시세 조회부터 주문까지 기초
코인 자동매매를 만들어 보려고 검색하면 대부분 "전략"부터 이야기합니다. 그런데 막상 코드를 짜려고 하면 거래소에 요청을 어떻게 보내는지에서 먼저 막힙니다. 이 글은 그 첫 관문만 다룹니다. 파이썬으로 업비트 API에 붙어서 시세를 읽고, 잔고를 확인하고, 주문을 넣는 데까지입니다.
0. 준비물
필요한 건 두 가지뿐입니다.
- 파이썬 패키지:
pip install requests pyjwt— requests는 HTTP 요청 라이브러리, PyJWT는 인증 토큰을 만드는 라이브러리입니다. - 업비트 API 키: 업비트 웹사이트 → 마이페이지 → Open API 관리에서 발급합니다. 발급할 때 허용 IP를 반드시 등록하고, 처음에는 자산조회·주문조회 권한만 켜세요. 주문 권한은 코드가 검증된 뒤에 켜도 늦지 않습니다.
발급받은 secret key는 발급 화면을 벗어나면 다시 볼 수 없습니다. 그리고 절대 소스코드에 직접 적지 마세요. 환경변수나 설정 파일로 분리합니다.
import os
ACCESS_KEY = os.environ["UPBIT_ACCESS_KEY"] # 실제 키는 환경변수로
SECRET_KEY = os.environ["UPBIT_SECRET_KEY"]
SERVER_URL = "https://api.upbit.com"
1. 시세 조회 — 인증이 필요 없는 요청
가장 쉬운 것부터 합니다. 현재가 조회는 인증 없이 됩니다. 그냥 GET 요청 하나면 끝입니다.
import requests
def get_price(market="KRW-BTC"):
url = f"{SERVER_URL}/v1/ticker"
res = requests.get(url, params={"markets": market}, timeout=5)
res.raise_for_status() # 4xx/5xx면 예외 발생
data = res.json()[0]
return {
"market": data["market"],
"price": data["trade_price"], # 현재가
"change": data["signed_change_rate"] * 100, # 전일 대비 %
}
print(get_price("KRW-BTC"))
# {'market': 'KRW-BTC', 'price': 95000000.0, 'change': 1.23}
여기서 market은 "결제통화-대상코인" 형식입니다. 원화로 비트코인을 사면 KRW-BTC, 원화로 이더리움이면 KRW-ETH입니다. 순서를 반대로 쓰는 실수가 은근히 잦습니다.
여러 종목을 한 번에 볼 수도 있습니다. markets 파라미터에 쉼표로 이어 붙이면 됩니다. 종목 20개를 20번 호출하지 말고 한 번에 묶으세요. 호출 횟수 제한을 아끼는 가장 쉬운 방법입니다.
2. 인증 요청 — JWT 토큰 만들기
잔고 조회부터는 "내가 누구인지" 증명해야 합니다. 업비트는 JWT라는 방식을 씁니다. 쉽게 말하면 내 access key와 일회용 번호를 secret key로 서명한 문자열을 헤더에 담아 보내는 것입니다.
규칙은 두 가지입니다.
- 파라미터가 없는 요청: access_key와 nonce(중복 방지용 임의 문자열)만 서명합니다.
- 파라미터가 있는 요청: 쿼리 문자열을 SHA512로 해시해서
query_hash로 함께 서명합니다.
import jwt, uuid, hashlib
from urllib.parse import urlencode
def make_token(params=None):
payload = {
"access_key": ACCESS_KEY,
"nonce": str(uuid.uuid4()), # 매 요청마다 새 값
}
if params:
query = urlencode(params)
payload["query_hash"] = hashlib.sha512(query.encode()).hexdigest()
payload["query_hash_alg"] = "SHA512"
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
return {"Authorization": f"Bearer {token}"}
주의할 점 하나. query_hash를 만들 때 쓴 쿼리 문자열과 실제로 보내는 쿼리 문자열이 글자 하나까지 같아야 합니다. 순서가 바뀌거나 인코딩이 달라지면 서버는 서명이 틀렸다고 판단합니다. 그래서 위 코드처럼 urlencode(params)로 만든 값을 그대로 재사용하는 게 안전합니다.
3. 잔고 조회
이제 토큰이 있으니 내 계좌를 볼 수 있습니다. 파라미터가 없는 요청이라 가장 단순합니다.
def get_balances():
res = requests.get(f"{SERVER_URL}/v1/accounts",
headers=make_token(), timeout=5)
res.raise_for_status()
return res.json()
for acc in get_balances():
print(acc["currency"], acc["balance"], acc["avg_buy_price"])
# KRW 150000.0 0
# BTC 0.00123 94000000
여기까지 성공했다면 인증이 제대로 붙은 겁니다. 만약 401이 뜬다면 십중팔구 허용 IP 미등록이거나 키 앞뒤 공백입니다. 설정 파일에서 키를 읽을 때 .strip() 한 번 해주세요.
4. 주문 넣기
주문은 POST에 파라미터가 붙으므로 query_hash가 필요합니다. 업비트 주문은 세 가지 조합을 기억하면 됩니다.
- 지정가:
ord_type="limit"+ price + volume - 시장가 매수:
ord_type="price"+ price(살 금액) — volume 없음 - 시장가 매도:
ord_type="market"+ volume(팔 수량) — price 없음
시장가 매수에서 price가 "수량"이 아니라 "쓸 원화 금액"이라는 점이 가장 헷갈리는 부분입니다.
def buy_market(market, krw_amount):
"""시장가 매수 — krw_amount 만큼의 원화로 산다"""
params = {
"market": market,
"side": "bid", # bid=매수, ask=매도
"ord_type": "price",
"price": str(krw_amount),
}
headers = make_token(params)
headers["Content-Type"] = "application/json"
res = requests.post(f"{SERVER_URL}/v1/orders",
json=params, headers=headers, timeout=5)
if res.status_code >= 400:
print("주문 실패:", res.status_code, res.text)
return None
return res.json()
# order = buy_market("KRW-BTC", 6000) # 6,000원어치 매수
위 코드에서 query_hash는 urlencode(params)로 만들었는데 실제 전송은 JSON body입니다. 업비트는 이 조합을 허용합니다. 다만 params의 키·값이 완전히 동일해야 하므로, 딕셔너리를 만든 뒤에는 손대지 마세요.
5. 실전에서 걸리는 것들
- 최소 주문 금액: 원화 마켓은 5,000원 이상이어야 주문이 들어갑니다. 테스트할 때 1,000원으로 넣고 "왜 안 되지" 하는 경우가 많습니다.
- 호출 횟수 제한: 초당 요청 수에 제한이 있습니다. 넘기면 429가 돌아옵니다. 반복문에
time.sleep(0.1)정도는 기본으로 넣고 시작하세요. - 수량 소수점: 코인 수량은 소수점 8자리까지입니다. 계산 결과를 그대로 넣으면 부동소수점 오차로 거부될 수 있어
round(qty, 8)로 다듬습니다. - 주문 즉시 체결이 아니다: 주문 API가 200을 돌려줘도 그건 "접수됨"입니다. 실제 체결은
/v1/order?uuid=...로 다시 확인해야 합니다. - 읽기 전용으로 먼저: 주문 함수를 처음 붙였을 때는 실제 호출 대신
print만 하는 모의 모드를 두는 걸 권합니다. 로직 버그는 대부분 첫 주 안에 드러납니다.
정리
순서만 다시 짚으면 키 발급 → 시세 조회(인증 없음) → JWT 토큰 → 잔고 조회 → 주문입니다. 각 단계가 확인되기 전에는 다음으로 넘어가지 마세요. 특히 잔고 조회가 되기 전에 주문 코드를 짜는 건 시간 낭비입니다. 인증이 안 되는 것인지 주문 파라미터가 틀린 것인지 구분이 안 되니까요.
여기까지가 자동매매의 "손발"입니다. 두뇌에 해당하는 전략은 그다음 문제고, 사실 손발이 튼튼한 쪽이 훨씬 오래 갑니다.
이 글은 교육·정보 목적이며 투자 권유가 아닙니다. 암호화폐 자동매매에는 원금 손실 위험이 있으며, API 키 관리 부주의로 인한 자산 손실 가능성도 있습니다. 모든 판단과 책임은 본인에게 있습니다.
댓글
댓글 쓰기