거래소 API란?
거래소 API(Application Programming Interface)는 프로그램이 거래소와 직접 통신하여 시세 조회, 주문 실행, 잔고 확인 등을 자동으로 수행할 수 있게 해주는 인터페이스입니다. 자동매매 봇을 만들려면 거래소 API 연동이 첫 번째 관문입니다.
이 글에서는 바이낸스·업비트 등 주요 거래소의 API 구조를 이해하고, 파이썬으로 안전하게 연동하는 방법을 실전 코드와 함께 다룹니다.
API 키 발급과 보안 설정
거래소 API를 사용하려면 먼저 API Key와 Secret Key를 발급받아야 합니다. 이 키는 계좌에 대한 전권 접근 권한을 부여하므로 보안이 극도로 중요합니다.
API 키 보안 체크리스트
- IP 화이트리스트: 반드시 봇이 실행되는 서버 IP만 허용합니다. 모든 IP 허용은 해킹 시 즉시 자산 탈취로 이어집니다.
- 출금 권한 비활성화: 자동매매에 출금 기능은 불필요합니다. 거래(trade) 권한만 부여하세요.
- 환경변수 관리: 코드에 키를 직접 하드코딩하지 마세요. 환경변수 또는 비밀 관리 도구를 사용합니다.
- 키 주기적 교체: 최소 3개월마다 API 키를 재발급합니다.
import os
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv('BINANCE_API_KEY')
API_SECRET = os.getenv('BINANCE_API_SECRET')
# .env 파일 (절대 git에 커밋하지 말 것)
# BINANCE_API_KEY=your_api_key_here
# BINANCE_API_SECRET=your_secret_here
# .gitignore에 반드시 추가
# .env
ccxt로 멀티 거래소 연동
ccxt는 100개 이상의 거래소를 통일된 인터페이스로 연동할 수 있는 파이썬 라이브러리입니다. 거래소마다 다른 API 규격을 추상화해주므로, 한 번 코드를 작성하면 거래소를 쉽게 변경할 수 있습니다.
import ccxt
# 거래소 객체 생성
exchange = ccxt.binance({
'apiKey': API_KEY,
'secret': API_SECRET,
'options': {'defaultType': 'future'}, # 선물 거래
'enableRateLimit': True, # 속도 제한 자동 준수
})
# 시세 조회
ticker = exchange.fetch_ticker('BTC/USDT')
print(f"현재가: {ticker['last']}")
print(f"24h 거래량: {ticker['quoteVolume']}")
# OHLCV 캔들 데이터
candles = exchange.fetch_ohlcv('BTC/USDT', '1h', limit=100)
print(f"최근 100개 1시간 캔들 로드 완료")
잔고 조회
# 잔고 확인
balance = exchange.fetch_balance()
usdt_free = balance['USDT']['free']
usdt_total = balance['USDT']['total']
print(f"사용 가능: {usdt_free} USDT")
print(f"총 잔고: {usdt_total} USDT")
주문 실행: 시장가 vs 지정가
자동매매에서 주문 방식의 선택은 수익에 직접적인 영향을 미칩니다. 각 주문 유형의 장단점을 정확히 이해해야 합니다.
| 주문 유형 | 장점 | 단점 | 적합한 상황 |
|---|---|---|---|
| 시장가(Market) | 즉시 체결 보장 | 슬리피지 발생 | 급변 시 즉각 대응 |
| 지정가(Limit) | 원하는 가격 체결, 낮은 수수료 | 미체결 가능 | 특정 가격대 진입 |
| 스탑마켓(Stop Market) | 손절 자동화 | 갭 발생 시 불리 | 손절/익절 자동 실행 |
| IOC/FOK | 부분 체결 방지 | 전량 취소 가능 | 정확한 수량 필요 시 |
# 시장가 매수
def market_buy(symbol, amount):
try:
order = exchange.create_market_buy_order(symbol, amount)
print(f"체결: {order['filled']} @ {order['average']}")
return order
except ccxt.InsufficientFunds:
print("잔고 부족")
except ccxt.NetworkError as e:
print(f"네트워크 오류: {e}")
except ccxt.ExchangeError as e:
print(f"거래소 오류: {e}")
# 지정가 매수
def limit_buy(symbol, amount, price):
order = exchange.create_limit_buy_order(symbol, amount, price)
print(f"주문 등록: {order['id']}")
return order
# 손절 주문 (스탑 마켓)
def set_stop_loss(symbol, amount, stop_price):
params = {'stopPrice': stop_price}
order = exchange.create_order(
symbol, 'stop_market', 'sell', amount, None, params
)
return order
에러 핸들링과 재시도 전략
실전 자동매매에서 API 호출은 반드시 실패합니다. 네트워크 타임아웃, 거래소 점검, 속도 제한 등 다양한 이유로 에러가 발생합니다. 견고한 에러 핸들링 없이는 봇이 예기치 않게 멈추거나 잘못된 포지션을 방치하게 됩니다.
import time
from functools import wraps
def retry_on_error(max_retries=3, delay=1, backoff=2):
"""지수 백오프 재시도 데코레이터"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
retries = 0
current_delay = delay
while retries < max_retries:
try:
return func(*args, **kwargs)
except ccxt.NetworkError as e:
retries += 1
print(f"네트워크 오류 ({retries}/{max_retries}): {e}")
time.sleep(current_delay)
current_delay *= backoff
except ccxt.RateLimitExceeded:
retries += 1
print(f"속도 제한 초과, {current_delay}초 대기")
time.sleep(current_delay)
current_delay *= backoff
except ccxt.ExchangeNotAvailable:
retries += 1
print(f"거래소 점검 중, {current_delay}초 대기")
time.sleep(current_delay * 5)
raise Exception(f"{max_retries}회 재시도 실패")
return wrapper
return decorator
@retry_on_error(max_retries=5, delay=2)
def safe_fetch_ticker(symbol):
return exchange.fetch_ticker(symbol)
주문 상태 확인과 동기화
주문을 넣은 뒤 반드시 체결 여부를 확인해야 합니다. 네트워크 오류로 응답을 못 받았더라도 거래소에서는 주문이 체결되었을 수 있습니다.
def check_order_status(symbol, order_id):
"""주문 상태 확인 및 동기화"""
order = exchange.fetch_order(order_id, symbol)
status = order['status'] # open, closed, canceled
if status == 'closed':
print(f"체결 완료: {order['filled']} @ {order['average']}")
elif status == 'open':
elapsed = time.time() - order['timestamp'] / 1000
if elapsed > 60: # 1분 초과 미체결
exchange.cancel_order(order_id, symbol)
print("미체결 주문 취소")
return order
웹소켓으로 실시간 데이터 수신
REST API는 요청할 때마다 데이터를 가져오지만, 웹소켓(WebSocket)은 실시간으로 데이터를 스트리밍합니다. 빠른 반응이 필요한 자동매매에서는 웹소켓이 필수입니다.
import ccxt.pro as ccxtpro
import asyncio
async def watch_orderbook():
exchange = ccxtpro.binance({
'apiKey': API_KEY,
'secret': API_SECRET,
'options': {'defaultType': 'future'},
})
while True:
try:
ob = await exchange.watch_order_book('BTC/USDT')
best_bid = ob['bids'][0][0]
best_ask = ob['asks'][0][0]
spread = best_ask - best_bid
print(f"매수: {best_bid} | 매도: {best_ask} | 스프레드: {spread}")
except Exception as e:
print(f"오류: {e}")
await asyncio.sleep(1)
asyncio.run(watch_orderbook())
Rate Limit 관리
거래소마다 분당 API 호출 횟수 제한이 있습니다. 이를 초과하면 일시적 차단(ban)을 당할 수 있으므로 반드시 관리해야 합니다.
- 바이낸스: 분당 1,200 요청 (가중치 기반)
- 업비트: 초당 10회 (주문), 분당 600회 (조회)
- 바이비트: 5초당 120 요청
ccxt의 enableRateLimit: True 옵션을 사용하면 자동으로 호출 간격을 조절합니다. 하지만 여러 봇을 동시에 운용할 때는 직접 속도를 제어하는 것이 안전합니다.
실전 운용 아키텍처
안정적인 자동매매 시스템은 단순한 스크립트가 아니라 체계적인 아키텍처가 필요합니다.
- 전략 모듈: 시그널 생성 로직. 모멘텀 전략이나 평균회귀 등 교체 가능하도록 설계합니다.
- 실행 모듈: 주문 실행, 에러 핸들링, 재시도 로직을 담당합니다.
- 리스크 모듈: 포지션 크기 제한, 계좌 생존 규칙 기반 손실 한도를 강제합니다.
- 모니터링: Telegram 알림, 로그 수집, 일일 성과 리포트 자동화.
마무리
거래소 API 연동은 자동매매의 기초이자 가장 중요한 토대입니다. API 키 보안, 에러 핸들링, Rate Limit 관리 등 기본기를 탄탄히 해야 전략이 아무리 좋아도 실전에서 무너지지 않습니다. ccxt로 빠르게 시작하되, 실전 운용에서는 웹소켓과 재시도 로직을 반드시 갖추세요.