이미지 로딩 중...
AI Generated
2025. 11. 12. · 7 Views
Python으로 알고리즘 트레이딩 봇 만들기 2편 - 바이낸스 API 연동하기
바이낸스 거래소 API를 Python과 연동하여 실시간 시세 조회, 주문 생성, 잔고 확인 등 트레이딩 봇의 핵심 기능을 구현하는 방법을 단계별로 배워봅니다. API 키 발급부터 실전 코드까지 모든 과정을 다룹니다.
목차
- 바이낸스 API 키 발급 및 환경 설정
- 실시간 시세 데이터 조회하기
- 계좌 잔고 및 자산 조회하기
- 시장가 주문 생성하기
- 지정가 주문 생성 및 관리하기
- 과거 캔들스틱 데이터 조회하기
- 주문 체결 내역 조회하기
- 웹소켓으로 실시간 시세 스트리밍
- 에러 처리 및 재시도 로직 구현
- 환경별 설정 관리 (테스트넷 vs 실전)
1. 바이낸스 API 키 발급 및 환경 설정
시작하며
여러분이 트레이딩 봇을 만들려고 할 때 가장 먼저 마주하는 장벽이 무엇인지 아시나요? 바로 거래소와의 연결입니다.
코드는 준비되었는데 거래소에 접근할 수 없다면 아무것도 할 수 없죠. 이런 문제는 실제 개발 현장에서 자주 발생합니다.
API 키를 잘못 발급받거나, 권한 설정을 제대로 하지 않으면 보안 위험에 노출되거나 원하는 기능을 사용할 수 없게 됩니다. 특히 암호화폐 트레이딩처럼 실제 자산이 오가는 경우에는 더욱 신중해야 합니다.
바로 이럴 때 필요한 것이 올바른 API 키 발급과 환경 설정입니다. 보안을 유지하면서도 필요한 기능에 접근할 수 있는 방법을 알아보겠습니다.
개요
간단히 말해서, API 키는 여러분의 거래소 계정에 프로그램이 접근할 수 있도록 하는 디지털 열쇠입니다. 하지만 진짜 열쇠와 달리 복사가 가능하기 때문에 절대 외부에 노출되어선 안 됩니다.
바이낸스 API는 읽기 전용, 거래 가능, 출금 가능 등 세분화된 권한을 제공합니다. 트레이딩 봇 개발 시에는 처음에는 읽기 전용으로 시작하고, 테스트가 완료된 후에만 거래 권한을 부여하는 것이 안전합니다.
기존에는 API 키를 코드에 직접 하드코딩했다면, 이제는 환경 변수나 별도의 설정 파일을 사용하여 관리합니다. 이렇게 하면 GitHub에 코드를 올려도 API 키가 노출되지 않습니다.
API 키는 API Key와 Secret Key 두 가지로 구성되며, API Key는 공개 식별자, Secret Key는 비밀번호 역할을 합니다. Secret Key는 한 번만 표시되므로 반드시 안전한 곳에 저장해야 합니다.
코드 예제
# .env 파일에 API 키 저장 (절대 Git에 커밋하지 말 것!)
# BINANCE_API_KEY=your_api_key_here
# BINANCE_SECRET_KEY=your_secret_key_here
import os
from dotenv import load_dotenv
from binance.client import Client
# 환경 변수 로드
load_dotenv()
# API 키를 환경 변수에서 안전하게 가져오기
api_key = os.getenv('BINANCE_API_KEY')
secret_key = os.getenv('BINANCE_SECRET_KEY')
# 바이낸스 클라이언트 초기화
client = Client(api_key, secret_key, testnet=True) # testnet=True로 테스트 환경 사용
# 연결 테스트
server_time = client.get_server_time()
print(f"바이낸스 서버 연결 성공! 서버 시간: {server_time}")
설명
이것이 하는 일: 바이낸스 거래소에 안전하게 접근하기 위한 API 키를 설정하고, Python 환경에서 이를 안전하게 로드하여 클라이언트 객체를 생성합니다. 첫 번째로, dotenv 라이브러리를 사용하여 .env 파일에서 환경 변수를 로드합니다.
이 방식은 API 키를 코드와 분리하여 보안을 강화하는 업계 표준 방법입니다. .env 파일은 반드시 .gitignore에 추가하여 버전 관리 시스템에 포함되지 않도록 해야 합니다.
그 다음으로, os.getenv() 함수가 실행되면서 시스템 환경 변수에서 API 키와 시크릿 키를 가져옵니다. 만약 키가 없으면 None을 반환하므로, 실제 프로덕션 코드에서는 예외 처리를 추가해야 합니다.
이렇게 하면 코드를 공유하거나 오픈소스로 공개할 때도 민감한 정보가 노출되지 않습니다. 마지막으로, Client 객체를 생성할 때 testnet=True 옵션을 사용하여 테스트 환경에 연결합니다.
테스트넷은 실제 돈이 오가지 않는 모의 거래 환경으로, 봇의 로직을 안전하게 검증할 수 있습니다. get_server_time()으로 연결을 테스트하여 API 키가 올바르게 설정되었는지 확인합니다.
여러분이 이 코드를 사용하면 실제 자금 손실 없이 트레이딩 봇을 개발하고 테스트할 수 있으며, 보안 모범 사례를 따르는 전문적인 개발 환경을 구축할 수 있습니다. 또한 팀원들과 협업할 때도 각자의 API 키를 안전하게 관리할 수 있습니다.
실전 팁
💡 .gitignore 파일에 반드시 .env를 추가하세요. 실수로 GitHub에 API 키를 올리면 몇 분 안에 봇들이 자동으로 탐지하여 계정이 해킹될 수 있습니다.
💡 IP 주소 화이트리스트 기능을 활용하세요. 바이낸스 API 키 설정에서 특정 IP만 접근하도록 제한하면 키가 유출되어도 다른 곳에서 사용할 수 없습니다.
💡 처음에는 반드시 읽기 전용 권한으로 시작하세요. 코드가 안정화되고 충분히 테스트한 후에만 거래 권한을 추가하는 것이 안전합니다.
💡 테스트넷에서 최소 1주일은 봇을 돌려보세요. 다양한 시장 상황에서의 동작을 확인해야 실전에서 예상치 못한 문제를 방지할 수 있습니다.
💡 API 키는 주기적으로 재발급하세요. 보안을 위해 3-6개월마다 새 키로 교체하는 것을 권장합니다.
2. 실시간 시세 데이터 조회하기
시작하며
여러분이 트레이딩 봇을 만들 때 가장 기본이 되는 데이터가 무엇일까요? 바로 실시간 가격 정보입니다.
현재 비트코인이 얼마인지, 이더리움의 24시간 변동폭은 어떤지 알지 못하면 어떤 트레이딩 전략도 실행할 수 없습니다. 이런 문제는 실제 개발 현장에서 자주 발생합니다.
시세 데이터를 받아오는 방법을 모르거나, 데이터 형식을 제대로 파싱하지 못하면 잘못된 판단으로 이어져 손실을 볼 수 있습니다. 특히 실시간 트레이딩에서는 1초의 지연도 큰 차이를 만들 수 있습니다.
바로 이럴 때 필요한 것이 바이낸스의 시세 조회 API입니다. 간단한 함수 호출만으로 실시간 가격, 거래량, 변동률 등 다양한 시장 데이터를 받아올 수 있습니다.
개요
간단히 말해서, 시세 조회 API는 거래소에서 현재 거래되고 있는 모든 암호화폐의 가격과 거래 정보를 실시간으로 가져오는 기능입니다. 이 데이터는 여러분의 트레이딩 봇이 의사결정을 내리는 기초 자료가 됩니다.
바이낸스는 다양한 시세 API를 제공합니다. 단일 코인의 현재가를 조회하는 get_symbol_ticker(), 24시간 통계를 받아오는 get_ticker(), 호가창 데이터를 가져오는 get_order_book() 등이 있습니다.
각 API는 용도에 따라 선택하여 사용할 수 있습니다. 기존에는 웹사이트를 크롤링하거나 복잡한 웹소켓 프로그래밍을 했다면, 이제는 python-binance 라이브러리의 간단한 메서드 호출만으로 모든 데이터를 받아올 수 있습니다.
API는 JSON 형식으로 구조화된 데이터를 반환하여 바로 사용할 수 있습니다. 시세 데이터에는 현재가뿐만 아니라 고가, 저가, 거래량, 가격 변동률 등 트레이딩에 필요한 모든 정보가 포함되어 있습니다.
이러한 데이터를 분석하여 매수/매도 신호를 생성하는 것이 알고리즘 트레이딩의 핵심입니다.
코드 예제
from binance.client import Client
import os
from dotenv import load_dotenv
load_dotenv()
client = Client(os.getenv('BINANCE_API_KEY'), os.getenv('BINANCE_SECRET_KEY'))
# 비트코인(BTC/USDT)의 현재 시세 조회
ticker = client.get_symbol_ticker(symbol="BTCUSDT")
print(f"BTC 현재가: ${ticker['price']}")
# 24시간 가격 변동 정보 조회
ticker_24h = client.get_ticker(symbol="BTCUSDT")
print(f"24시간 최고가: ${ticker_24h['highPrice']}")
print(f"24시간 최저가: ${ticker_24h['lowPrice']}")
print(f"24시간 거래량: {ticker_24h['volume']} BTC")
print(f"가격 변동률: {ticker_24h['priceChangePercent']}%")
# 여러 코인의 시세를 한 번에 조회
symbols = ['BTCUSDT', 'ETHUSDT', 'BNBUSDT']
for symbol in symbols:
price = client.get_symbol_ticker(symbol=symbol)
print(f"{symbol}: ${price['price']}")
설명
이것이 하는 일: 바이낸스 거래소에서 암호화폐의 실시간 가격과 24시간 거래 통계를 조회하여 트레이딩 봇의 의사결정에 필요한 데이터를 수집합니다. 첫 번째로, get_symbol_ticker() 함수는 특정 거래 쌍(예: BTCUSDT)의 현재 가격만을 빠르게 조회합니다.
이 함수는 가장 가볍고 빠른 API로, 단순히 현재가만 필요할 때 사용합니다. 반환되는 데이터는 딕셔너리 형태이며 'symbol'과 'price' 키를 포함합니다.
이 방식은 API 호출 제한을 최소화하면서 필요한 정보를 얻을 수 있습니다. 그 다음으로, get_ticker() 함수가 실행되면서 훨씬 더 풍부한 정보를 제공합니다.
24시간 동안의 최고가, 최저가, 거래량, 가격 변동폭, 변동률 등 다양한 통계 데이터를 한 번에 받아옵니다. 이 데이터는 시장의 변동성을 파악하고 추세를 분석하는 데 필수적입니다.
예를 들어, 가격 변동률이 10% 이상이면 변동성이 높다고 판단하여 거래 전략을 조정할 수 있습니다. 마지막으로, 여러 코인의 시세를 반복문으로 조회하는 방법을 보여줍니다.
포트폴리오를 다각화하는 트레이딩 봇이라면 여러 자산의 가격을 동시에 모니터링해야 합니다. 다만 API 호출 제한(분당 1200회)을 고려하여 너무 자주 호출하지 않도록 주의해야 합니다.
여러분이 이 코드를 사용하면 실시간으로 시장 상황을 파악하고, 데이터 기반의 트레이딩 결정을 내릴 수 있습니다. 또한 여러 코인을 모니터링하여 최적의 투자 기회를 찾을 수 있으며, 24시간 통계를 활용해 시장의 변동성과 추세를 분석할 수 있습니다.
실전 팁
💡 API 호출 제한을 항상 확인하세요. 바이낸스는 분당 1200회, IP당 일일 요청 수를 제한합니다. 초과 시 일시적으로 차단될 수 있으므로 적절한 딜레이를 추가하세요.
💡 가격 데이터는 문자열로 반환됩니다. 계산에 사용하기 전에 반드시 float(ticker['price'])로 변환하세요. 그렇지 않으면 타입 에러가 발생합니다.
💡 24시간 통계는 캐시될 수 있으므로 초단위 정밀도가 필요하다면 웹소켓을 사용하세요. REST API는 1-2초의 지연이 있을 수 있습니다.
💡 여러 코인을 조회할 때는 get_all_tickers() 함수로 한 번에 모든 시세를 받아오는 것이 효율적입니다. 개별 호출보다 API 제한을 덜 소모합니다.
💡 네트워크 오류에 대비해 try-except 블록으로 예외 처리를 추가하세요. 인터넷 연결이 끊기거나 API 서버에 문제가 생길 수 있습니다.
3. 계좌 잔고 및 자산 조회하기
시작하며
여러분이 트레이딩 봇을 운영할 때 "지금 내가 얼마를 가지고 있지?"라는 질문에 바로 답할 수 있나요? 수동으로 거래소에 로그인해서 확인하는 것은 자동화의 의미가 없습니다.
이런 문제는 실제 개발 현장에서 자주 발생합니다. 현재 보유 자산을 모르면 얼마나 매수할 수 있는지, 포지션 사이즈를 어떻게 조절할지 결정할 수 없습니다.
특히 여러 코인을 동시에 거래하는 경우 자산 현황을 정확히 파악하는 것이 더욱 중요합니다. 바로 이럴 때 필요한 것이 계좌 정보 조회 API입니다.
현재 보유한 모든 암호화폐의 수량과 가치를 프로그래밍 방식으로 확인할 수 있습니다.
개요
간단히 말해서, 계좌 조회 API는 여러분의 바이낸스 지갑에 있는 모든 자산의 목록과 수량을 가져오는 기능입니다. 마치 은행 앱에서 잔고를 확인하는 것과 같지만, 이것은 코드로 자동화됩니다.
바이낸스의 get_account() 함수는 계좌의 모든 정보를 반환합니다. 각 코인의 사용 가능한 잔고(free), 주문에 묶여있는 잔고(locked), 그리고 계좌 권한 등의 정보가 포함됩니다.
트레이딩 봇은 이 정보를 바탕으로 매수 가능 금액을 계산하고 주문 크기를 결정합니다. 기존에는 웹 페이지를 직접 열어서 눈으로 확인했다면, 이제는 코드 한 줄로 모든 자산 정보를 받아와서 로그에 기록하거나 데이터베이스에 저장할 수 있습니다.
이렇게 하면 자산 변동 내역을 추적하고 수익률을 분석하는 것도 가능합니다. 계좌 정보에는 마지막 업데이트 시간도 포함되어 있어 데이터의 신선도를 확인할 수 있습니다.
또한 거래 수수료 할인 레벨 등의 정보도 함께 제공되어 정확한 수익률 계산이 가능합니다.
코드 예제
from binance.client import Client
import os
from dotenv import load_dotenv
load_dotenv()
client = Client(os.getenv('BINANCE_API_KEY'), os.getenv('BINANCE_SECRET_KEY'))
# 전체 계좌 정보 조회
account = client.get_account()
# 계좌 권한 확인
print(f"계좌 타입: {account['accountType']}")
print(f"거래 가능 여부: {account['canTrade']}")
# 보유 중인 자산만 필터링 (잔고가 0보다 큰 것만)
balances = account['balances']
my_assets = [asset for asset in balances if float(asset['free']) > 0 or float(asset['locked']) > 0]
# 보유 자산 출력
print("\n=== 보유 자산 현황 ===")
for asset in my_assets:
print(f"{asset['asset']}: {asset['free']} (사용가능) + {asset['locked']} (주문중)")
# 특정 코인(예: BTC) 잔고만 조회
btc_balance = client.get_asset_balance(asset='BTC')
print(f"\nBTC 잔고: {btc_balance['free']} BTC")
설명
이것이 하는 일: 바이낸스 계좌의 모든 자산 정보를 조회하여 현재 보유량을 파악하고, 거래 가능한 금액을 계산하여 트레이딩 봇의 주문 크기를 결정합니다. 첫 번째로, get_account() 함수는 계좌의 전체 스냅샷을 가져옵니다.
이 함수는 API 가중치가 높은 편(10)이므로 너무 자주 호출하지 않는 것이 좋습니다. 반환되는 데이터에는 'accountType', 'canTrade', 'canWithdraw' 등의 권한 정보와 'balances' 배열이 포함됩니다.
권한 정보를 확인하여 API 키가 올바른 권한을 가지고 있는지 검증할 수 있습니다. 그 다음으로, 리스트 컴프리헨션을 사용하여 잔고가 있는 자산만 필터링합니다.
바이낸스는 모든 상장 코인(500개 이상)의 잔고를 반환하는데, 대부분은 0이므로 실제 보유 자산만 추출하는 것이 효율적입니다. 'free'는 즉시 거래 가능한 수량, 'locked'는 미체결 주문에 묶여있는 수량을 의미합니다.
실제 매수 가능 금액을 계산할 때는 'free' 값만 사용해야 합니다. 마지막으로, get_asset_balance() 함수로 특정 코인의 잔고만 빠르게 조회할 수 있습니다.
이 함수는 전체 계좌 정보를 가져오는 것보다 가볍고 빠르므로, 특정 코인의 잔고만 필요할 때 유용합니다. 예를 들어 BTC/USDT 거래 전에 USDT 잔고만 확인하면 되는 경우에 사용합니다.
여러분이 이 코드를 사용하면 실시간으로 자산 현황을 파악하여 포트폴리오를 관리할 수 있고, 매수 가능 금액을 자동으로 계산하여 안전하게 주문을 생성할 수 있습니다. 또한 자산 변동 내역을 로깅하여 수익률 분석과 리스크 관리에 활용할 수 있습니다.
실전 팁
💡 get_account()는 API 가중치가 10으로 높으므로 1분에 한 번 정도만 호출하세요. 실시간 잔고가 필요하다면 주문 체결 시에만 업데이트하는 방식이 효율적입니다.
💡 잔고 값은 모두 문자열로 반환됩니다. 계산에 사용하기 전에 반드시 float()로 변환하세요. Decimal 타입을 사용하면 부동소수점 오차를 방지할 수 있습니다.
💡 주문 생성 전에 반드시 'free' 잔고를 확인하세요. 'locked' 잔고는 사용할 수 없으므로 둘을 합산하면 주문 실패가 발생합니다.
💡 잔고 정보를 주기적으로 로그 파일이나 데이터베이스에 저장하세요. 시간대별 자산 변동을 추적하면 봇의 성과를 분석하고 개선점을 찾을 수 있습니다.
💡 여러 자산을 동시에 거래한다면 딕셔너리로 잔고를 캐싱하세요. API 호출을 줄이고 빠른 조회가 가능합니다.
4. 시장가 주문 생성하기
시작하며
여러분이 트레이딩 봇을 만들었는데 실제로 거래를 할 수 없다면 무슨 소용이 있을까요? 시세를 조회하고 분석하는 것은 준비 단계일 뿐, 진짜 목적은 자동으로 매수와 매도를 실행하는 것입니다.
이런 문제는 실제 개발 현장에서 자주 발생합니다. 주문 API의 복잡한 파라미터를 이해하지 못하거나, 최소 주문 수량 같은 거래소 규칙을 모르면 주문이 실패하거나 예상치 못한 결과가 나올 수 있습니다.
특히 실전에서는 한 번의 실수가 실제 금전적 손실로 이어집니다. 바로 이럴 때 필요한 것이 시장가 주문(Market Order) API입니다.
현재 시장 가격으로 즉시 체결되는 주문을 코드 몇 줄로 실행할 수 있습니다.
개요
간단히 말해서, 시장가 주문은 "지금 당장 이 가격에 사겠다/팔겠다"는 주문입니다. 가격을 지정하지 않고 현재 시장에서 거래 가능한 최적의 가격으로 즉시 체결됩니다.
시장가 주문은 빠른 체결이 필요할 때 사용합니다. 예를 들어, 급등/급락 시 빠르게 포지션을 진입하거나 청산해야 할 때 유용합니다.
반면 지정가 주문(Limit Order)은 원하는 가격에 도달했을 때만 체결되므로 체결이 보장되지 않습니다. 기존에는 거래소 웹사이트에서 수동으로 주문을 클릭했다면, 이제는 order_market_buy()와 order_market_sell() 함수로 밀리초 단위의 빠른 주문이 가능합니다.
특히 알고리즘 트레이딩에서는 이런 속도 차이가 수익률을 크게 좌우합니다. 바이낸스는 각 거래 쌍마다 최소 주문 수량, 최소 주문 금액, 소수점 자리수 등의 제약 조건을 가지고 있습니다.
이런 제약을 어기면 주문이 거부되므로 반드시 확인해야 합니다.
코드 예제
from binance.client import Client
from binance.enums import *
import os
from dotenv import load_dotenv
load_dotenv()
client = Client(os.getenv('BINANCE_API_KEY'), os.getenv('BINANCE_SECRET_KEY'))
# 시장가 매수 주문 (수량 지정)
# 0.001 BTC를 현재 시장가로 매수
try:
order = client.order_market_buy(
symbol='BTCUSDT',
quantity=0.001 # BTC 수량
)
print(f"매수 주문 성공! 주문 ID: {order['orderId']}")
print(f"체결 가격: {order['fills'][0]['price']}")
print(f"수수료: {order['fills'][0]['commission']} {order['fills'][0]['commissionAsset']}")
except Exception as e:
print(f"주문 실패: {e}")
# 시장가 매도 주문
# 보유한 0.001 BTC를 현재 시장가로 매도
try:
order = client.order_market_sell(
symbol='BTCUSDT',
quantity=0.001
)
print(f"매도 주문 성공! 주문 ID: {order['orderId']}")
except Exception as e:
print(f"주문 실패: {e}")
설명
이것이 하는 일: 바이낸스 거래소에서 현재 시장 가격으로 암호화폐를 즉시 매수하거나 매도하는 주문을 생성하고 실행합니다. 첫 번째로, order_market_buy() 함수는 지정한 수량만큼 즉시 매수 주문을 실행합니다.
symbol 파라미터는 거래 쌍(예: BTCUSDT), quantity는 매수할 암호화폐의 수량을 지정합니다. 주문이 성공하면 orderId, 체결 가격, 수수료 등의 정보가 담긴 딕셔너리를 반환합니다.
시장가 주문은 호가창의 여러 가격대에서 체결될 수 있으므로 'fills' 배열에 여러 체결 내역이 포함될 수 있습니다. 그 다음으로, try-except 블록으로 예외 처리를 구현합니다.
주문 실패의 원인은 다양합니다: 잔고 부족, 최소 주문 수량 미달, API 권한 부족, 네트워크 오류 등. 실전 트레이딩 봇에서는 각 오류 유형별로 적절한 대응 로직을 구현해야 합니다.
예를 들어 잔고 부족이면 주문 크기를 줄이고, 네트워크 오류면 재시도하는 식입니다. 마지막으로, order_market_sell() 함수로 보유한 암호화폐를 매도합니다.
매도 시에는 반드시 보유 수량 이하로만 주문해야 합니다. 앞서 배운 get_asset_balance()로 현재 보유량을 확인한 후 주문하는 것이 안전합니다.
매도 후에는 USDT 같은 기준 통화가 증가합니다. 여러분이 이 코드를 사용하면 트레이딩 신호가 발생했을 때 자동으로 주문을 실행할 수 있고, 수동 거래보다 훨씬 빠른 속도로 시장 기회를 포착할 수 있습니다.
또한 주문 내역을 로그로 기록하여 거래 히스토리를 분석하고 전략을 개선할 수 있습니다.
실전 팁
💡 시장가 주문은 슬리피지(가격 미끄러짐)가 발생할 수 있습니다. 특히 거래량이 적은 코인은 예상보다 불리한 가격에 체결될 수 있으므로 주의하세요.
💡 최소 주문 수량은 거래 쌍마다 다릅니다. client.get_symbol_info('BTCUSDT')로 제약 조건을 확인하세요. 예를 들어 BTC는 0.00001 BTC가 최소 단위일 수 있습니다.
💡 주문 직후 반환되는 정보는 부분 체결 상태일 수 있습니다. client.get_order()로 최종 체결 내역을 다시 확인하는 것이 정확합니다.
💡 테스트 시에는 아주 작은 금액으로 시작하세요. 코드 한 줄의 실수가 큰 손실로 이어질 수 있으므로 충분히 검증한 후 금액을 늘리세요.
💡 주문 수수료를 고려하여 수익률을 계산하세요. 바이낸스는 기본 0.1% 수수료를 부과하며, BNB로 지불 시 할인받을 수 있습니다.
5. 지정가 주문 생성 및 관리하기
시작하며
여러분이 "비트코인이 50,000달러까지 떨어지면 사고 싶어"라고 생각한 적 있나요? 시장가 주문으로는 원하는 가격에 정확히 매수할 수 없습니다.
현재 가격이 아닌 특정 목표 가격에 주문을 걸어두고 싶을 때가 있습니다. 이런 문제는 실제 개발 현장에서 자주 발생합니다.
시장가로만 거래하면 변동성이 큰 시장에서 불리한 가격에 체결되어 손실을 볼 수 있습니다. 특히 대량 주문의 경우 지정가 주문으로 여러 가격대에 분산하는 전략이 필수적입니다.
바로 이럴 때 필요한 것이 지정가 주문(Limit Order)입니다. 원하는 가격을 지정하여 주문을 예약하고, 시장이 그 가격에 도달했을 때 자동으로 체결되도록 할 수 있습니다.
개요
간단히 말해서, 지정가 주문은 "이 가격에만 사겠다/팔겠다"라고 조건을 거는 주문입니다. 시장 가격이 지정한 가격에 도달하면 체결되고, 도달하지 않으면 계속 대기하거나 취소할 수 있습니다.
지정가 주문의 장점은 가격 통제입니다. 매수 시 지정 가격 이하로만, 매도 시 지정 가격 이상으로만 체결되므로 예상치 못한 불리한 가격으로 거래될 위험이 없습니다.
또한 메이커(Maker) 수수료가 적용되어 시장가 주문보다 수수료가 저렴하거나 리베이트를 받을 수도 있습니다. 기존에는 거래소에서 가격을 입력하고 수동으로 주문했다면, 이제는 order_limit_buy()와 order_limit_sell() 함수로 복잡한 주문 전략을 자동화할 수 있습니다.
예를 들어 여러 가격대에 분산 매수 주문을 걸어두는 스케일링 전략을 코드로 구현할 수 있습니다. 지정가 주문은 체결이 보장되지 않습니다.
시장이 지정 가격에 도달하지 않으면 영원히 체결되지 않을 수 있으므로, 미체결 주문을 주기적으로 확인하고 관리하는 로직이 필요합니다.
코드 예제
from binance.client import Client
from binance.enums import *
import os
from dotenv import load_dotenv
load_dotenv()
client = Client(os.getenv('BINANCE_API_KEY'), os.getenv('BINANCE_SECRET_KEY'))
# 지정가 매수 주문 - 45,000달러에 0.001 BTC 매수
try:
order = client.order_limit_buy(
symbol='BTCUSDT',
quantity=0.001, # 수량
price='45000.00' # 희망 매수 가격 (문자열로 전달)
)
print(f"지정가 매수 주문 생성! 주문 ID: {order['orderId']}")
print(f"상태: {order['status']}") # 보통 'NEW' 상태
except Exception as e:
print(f"주문 실패: {e}")
# 지정가 매도 주문 - 55,000달러에 0.001 BTC 매도
try:
order = client.order_limit_sell(
symbol='BTCUSDT',
quantity=0.001,
price='55000.00'
)
print(f"지정가 매도 주문 생성! 주문 ID: {order['orderId']}")
except Exception as e:
print(f"주문 실패: {e}")
# 미체결 주문 조회
open_orders = client.get_open_orders(symbol='BTCUSDT')
for order in open_orders:
print(f"주문 ID: {order['orderId']}, 가격: {order['price']}, 수량: {order['origQty']}")
# 주문 취소
if len(open_orders) > 0:
cancel_result = client.cancel_order(symbol='BTCUSDT', orderId=open_orders[0]['orderId'])
print(f"주문 취소됨: {cancel_result['orderId']}")
설명
이것이 하는 일: 원하는 가격을 지정하여 매수/매도 주문을 예약하고, 미체결 주문을 조회 및 관리하여 효율적인 트레이딩 전략을 실행합니다. 첫 번째로, order_limit_buy() 함수는 세 가지 필수 파라미터를 받습니다: symbol(거래 쌍), quantity(수량), price(희망 가격).
가격은 반드시 문자열로 전달해야 하며, 소수점 자리수는 거래 쌍의 규칙을 따라야 합니다. 예를 들어 BTCUSDT는 소수점 둘째 자리까지 허용합니다.
주문이 생성되면 상태는 'NEW'이며, 체결되면 'FILLED', 부분 체결되면 'PARTIALLY_FILLED'로 변경됩니다. 그 다음으로, get_open_orders() 함수로 현재 미체결 상태인 모든 주문을 조회합니다.
symbol을 지정하면 해당 거래 쌍의 주문만, 생략하면 모든 거래 쌍의 주문을 가져옵니다. 반환되는 정보에는 주문 ID, 가격, 원래 수량, 체결된 수량, 주문 시간 등이 포함됩니다.
트레이딩 봇은 이 정보를 주기적으로 확인하여 오래된 미체결 주문을 취소하거나 가격을 조정하는 로직을 구현할 수 있습니다. 마지막으로, cancel_order() 함수로 미체결 주문을 취소합니다.
symbol과 orderId를 모두 제공해야 하며, 이미 체결되었거나 취소된 주문은 에러를 발생시킵니다. 시장 상황이 변했을 때 기존 주문을 취소하고 새로운 가격으로 재주문하는 것은 트레이딩 봇의 핵심 기능 중 하나입니다.
모든 주문을 한 번에 취소하려면 cancel_all_open_orders() 함수를 사용할 수 있습니다. 여러분이 이 코드를 사용하면 원하는 가격에 정확히 매수/매도할 수 있고, 슬리피지를 최소화하여 수익률을 높일 수 있습니다.
또한 여러 가격대에 주문을 분산하는 스케일링 전략을 구현하여 리스크를 분산하고, 미체결 주문을 효율적으로 관리하여 자본 효율성을 높일 수 있습니다.
실전 팁
💡 지정가 주문의 가격은 현재 시장가로부터 너무 멀면 체결되기 어렵습니다. 현재가의 ±5% 이내로 설정하는 것이 일반적입니다.
💡 미체결 주문이 너무 오래 남아있으면 자본이 묶입니다. 예를 들어 24시간 이상 미체결 주문은 자동 취소하는 로직을 추가하세요.
💡 가격 정밀도를 확인하세요. BTCUSDT는 소수점 2자리, ETHUSDT는 소수점 2자리 등 거래 쌍마다 다릅니다. get_symbol_info()로 확인할 수 있습니다.
💡 지정가 주문은 메이커 수수료가 적용됩니다. 거래량이 많으면 VIP 등급에 따라 수수료 할인이나 리베이트를 받을 수 있어 수익률에 큰 영향을 줍니다.
💡 주문 취소 후 즉시 재주문하면 네트워크 지연으로 인해 중복 주문이 발생할 수 있습니다. 취소 확인 후 0.5초 정도 대기하는 것이 안전합니다.
6. 과거 캔들스틱 데이터 조회하기
시작하며
여러분이 "지난 한 달간 비트코인의 가격 추세는 어땠을까?"라는 질문에 답하고 싶다면 어떻게 해야 할까요? 현재가만으로는 과거의 패턴과 추세를 분석할 수 없습니다.
이런 문제는 실제 개발 현장에서 자주 발생합니다. 기술적 분석을 하려면 과거 가격 데이터가 필수적입니다.
이동평균선, RSI, 볼린저 밴드 같은 지표들은 모두 과거 가격 데이터를 기반으로 계산됩니다. 데이터 없이는 어떤 알고리즘 전략도 구현할 수 없습니다.
바로 이럴 때 필요한 것이 캔들스틱(OHLCV) 데이터 조회 API입니다. 시가, 고가, 저가, 종가, 거래량을 시간대별로 받아와서 차트 분석과 전략 백테스팅에 활용할 수 있습니다.
개요
간단히 말해서, 캔들스틱 데이터는 특정 시간 간격(1분, 5분, 1시간, 1일 등)동안의 가격 변동을 요약한 정보입니다. 각 캔들은 시가(Open), 고가(High), 저가(Low), 종가(Close), 거래량(Volume)의 5가지 값을 포함합니다.
바이낸스의 get_klines() 함수는 다양한 시간 간격의 캔들 데이터를 제공합니다. 1분봉부터 1개월봉까지 선택할 수 있으며, 최대 1000개의 캔들을 한 번에 조회할 수 있습니다.
이 데이터는 pandas DataFrame으로 변환하여 분석하기 쉽게 가공할 수 있습니다. 기존에는 거래소 차트를 눈으로 보며 패턴을 찾았다면, 이제는 수천 개의 캔들 데이터를 코드로 분석하여 객관적인 매매 신호를 생성할 수 있습니다.
머신러닝 모델을 학습시키거나 백테스팅을 하는 데도 이 데이터가 필수입니다. 캔들 데이터는 실시간으로 업데이트되므로, 가장 최근 캔들(현재 진행 중인 캔들)은 아직 완성되지 않은 상태입니다.
전략 개발 시 이 점을 고려하여 완성된 캔들만 사용하거나, 실시간 데이터를 별도로 처리해야 합니다.
코드 예제
from binance.client import Client
import pandas as pd
import os
from dotenv import load_dotenv
from datetime import datetime
load_dotenv()
client = Client(os.getenv('BINANCE_API_KEY'), os.getenv('BINANCE_SECRET_KEY'))
# 1시간봉 캔들 데이터 조회 (최근 100개)
klines = client.get_klines(
symbol='BTCUSDT',
interval=Client.KLINE_INTERVAL_1HOUR, # 1분, 5분, 15분, 1시간, 4시간, 1일 등 선택 가능
limit=100 # 최대 1000개
)
# 데이터를 pandas DataFrame으로 변환
df = pd.DataFrame(klines, columns=[
'timestamp', 'open', 'high', 'low', 'close', 'volume',
'close_time', 'quote_volume', 'trades', 'taker_buy_base',
'taker_buy_quote', 'ignore'
])
# 타임스탬프를 읽기 쉬운 날짜로 변환
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
# 가격 데이터를 float로 변환
df['close'] = df['close'].astype(float)
df['high'] = df['high'].astype(float)
df['low'] = df['low'].astype(float)
# 최근 5개 캔들 출력
print(df[['timestamp', 'open', 'high', 'low', 'close', 'volume']].tail())
# 간단한 이동평균선 계산
df['MA20'] = df['close'].rolling(window=20).mean()
print(f"\n현재 20시간 이동평균: ${df['MA20'].iloc[-1]:.2f}")
설명
이것이 하는 일: 과거의 가격 변동 데이터를 시간대별로 조회하여 기술적 지표를 계산하고, 트레이딩 전략을 개발하거나 백테스팅합니다. 첫 번째로, get_klines() 함수는 세 가지 주요 파라미터를 받습니다.
symbol은 거래 쌍, interval은 캔들의 시간 간격(1분, 5분, 1시간, 1일 등), limit은 조회할 캔들 개수입니다. 반환되는 데이터는 리스트의 리스트 형태로, 각 내부 리스트가 하나의 캔들을 나타냅니다.
첫 번째 요소는 타임스탬프, 그 다음이 시가, 고가, 저가, 종가, 거래량 순입니다. 그 다음으로, pandas DataFrame으로 변환하는 과정이 실행됩니다.
DataFrame은 데이터 분석에 최적화된 구조로, 컬럼명을 지정하여 가독성을 높입니다. 타임스탬프는 밀리초 단위의 Unix 시간이므로 pd.to_datetime()으로 사람이 읽을 수 있는 날짜 형식으로 변환합니다.
또한 모든 가격 데이터는 문자열로 반환되므로 float로 변환하여 수학적 계산이 가능하도록 합니다. 마지막으로, 실제 기술적 지표를 계산하는 예시입니다.
rolling(window=20).mean()은 최근 20개 캔들의 종가 평균을 계산하는 이동평균선(Moving Average)입니다. 이런 식으로 RSI, MACD, 볼린저 밴드 등 다양한 지표를 구현할 수 있습니다.
이동평균선이 현재가보다 낮으면 상승 추세, 높으면 하락 추세로 판단하는 것이 기본적인 트레이딩 신호입니다. 여러분이 이 코드를 사용하면 수개월, 수년치의 가격 데이터를 분석하여 패턴을 발견할 수 있고, 백테스팅으로 전략의 수익률을 검증한 후 실전에 투입할 수 있습니다.
또한 pandas와 matplotlib 등의 라이브러리를 결합하여 시각화하고, 머신러닝 모델의 학습 데이터로 활용할 수 있습니다.
실전 팁
💡 limit 파라미터는 최대 1000개입니다. 더 많은 데이터가 필요하면 startTime과 endTime을 사용하여 여러 번 호출하고 합쳐야 합니다.
💡 가장 최근 캔들은 아직 완성되지 않은 상태입니다. 전략 신호 생성 시 df.iloc[-2](마지막에서 두 번째)를 사용하여 완성된 캔들만 사용하세요.
💡 1분봉은 데이터가 방대하므로 장기 분석에는 부적합합니다. 백테스팅은 1시간봉이나 4시간봉, 일봉을 사용하는 것이 효율적입니다.
💡 거래량(volume) 데이터도 중요한 지표입니다. 가격 상승과 함께 거래량이 증가하면 강한 추세, 거래량이 감소하면 약한 추세로 해석됩니다.
💡 데이터를 CSV 파일로 저장해두면 API 호출을 줄일 수 있습니다. df.to_csv('btc_data.csv')로 저장하고 필요할 때 로드하세요.
7. 주문 체결 내역 조회하기
시작하며
여러분이 트레이딩 봇을 운영하면서 "내가 지금까지 어떤 거래를 했지?"라는 질문에 답할 수 없다면 문제입니다. 수익이 났는지, 어떤 전략이 잘 작동했는지 알 수 없기 때문입니다.
이런 문제는 실제 개발 현장에서 자주 발생합니다. 거래 내역을 추적하지 않으면 수익률 계산이 불가능하고, 버그가 발생해도 원인을 파악할 수 없습니다.
특히 세금 신고나 감사를 위해서는 모든 거래 내역이 정확히 기록되어야 합니다. 바로 이럴 때 필요한 것이 주문 체결 내역 조회 API입니다.
과거의 모든 매매 기록을 프로그래밍 방식으로 가져와서 분석하고 로깅할 수 있습니다.
개요
간단히 말해서, 주문 체결 내역은 여러분이 실제로 실행한 모든 거래의 기록입니다. 언제, 얼마에, 얼마나 샀는지/팔았는지, 수수료는 얼마였는지 등의 상세 정보가 포함됩니다.
바이낸스의 get_my_trades() 함수는 특정 거래 쌍의 모든 체결 내역을 시간순으로 반환합니다. 각 체결마다 고유한 tradeId가 있어 중복을 방지할 수 있고, 매수인지 매도인지도 명확히 구분됩니다.
이 데이터는 손익 계산, 세금 신고, 성과 분석에 필수적입니다. 기존에는 거래소 웹사이트에서 거래 내역을 일일이 확인하거나 CSV로 다운로드했다면, 이제는 API로 자동으로 가져와서 데이터베이스에 저장하고 실시간으로 분석할 수 있습니다.
이렇게 하면 매 거래마다 수익률을 자동 계산하고 알림을 보내는 것도 가능합니다. 거래 내역에는 수수료 정보도 포함되어 있습니다.
BNB로 수수료를 지불했는지, 할인을 받았는지 등이 기록되어 있어 정확한 순수익을 계산할 수 있습니다. 이 정보를 무시하면 실제 수익률과 계산된 수익률에 차이가 생깁니다.
코드 예제
from binance.client import Client
import pandas as pd
import os
from dotenv import load_dotenv
load_dotenv()
client = Client(os.getenv('BINANCE_API_KEY'), os.getenv('BINANCE_SECRET_KEY'))
# 최근 거래 내역 조회 (최대 1000개)
trades = client.get_my_trades(
symbol='BTCUSDT',
limit=100 # 조회할 거래 개수
)
# DataFrame으로 변환
df_trades = pd.DataFrame(trades)
# 주요 컬럼만 선택하여 출력
if not df_trades.empty:
df_trades['time'] = pd.to_datetime(df_trades['time'], unit='ms')
df_trades['price'] = df_trades['price'].astype(float)
df_trades['qty'] = df_trades['qty'].astype(float)
df_trades['commission'] = df_trades['commission'].astype(float)
print(df_trades[['time', 'price', 'qty', 'commission', 'commissionAsset', 'isBuyer']].tail(10))
# 총 거래 횟수 및 통계
total_trades = len(df_trades)
buy_trades = df_trades[df_trades['isBuyer'] == True]
sell_trades = df_trades[df_trades['isBuyer'] == False]
print(f"\n총 거래 횟수: {total_trades}")
print(f"매수 거래: {len(buy_trades)}, 매도 거래: {len(sell_trades)}")
print(f"평균 매수가: ${buy_trades['price'].mean():.2f}")
print(f"평균 매도가: ${sell_trades['price'].mean():.2f}")
else:
print("거래 내역이 없습니다.")
설명
이것이 하는 일: 과거에 실행된 모든 거래 체결 내역을 조회하여 수익률을 계산하고, 전략 성과를 분석하며, 세금 신고를 위한 데이터를 수집합니다. 첫 번째로, get_my_trades() 함수는 특정 거래 쌍의 체결 내역을 가져옵니다.
symbol 파라미터는 필수이며, limit으로 조회 개수를 지정할 수 있습니다(최대 1000개). startTime과 endTime을 사용하면 특정 기간의 거래만 필터링할 수 있습니다.
반환되는 각 거래 객체에는 time(체결 시간), price(체결 가격), qty(체결 수량), commission(수수료), isBuyer(매수 여부) 등의 필드가 포함됩니다. 그 다음으로, pandas DataFrame으로 변환하여 데이터를 분석하기 쉽게 만듭니다.
타임스탬프는 밀리초 단위이므로 datetime으로 변환하고, 모든 숫자 데이터는 문자열에서 float로 변환합니다. isBuyer 필드는 True면 매수, False면 매도를 의미하며, 이를 기준으로 거래를 분류할 수 있습니다.
commission과 commissionAsset을 보면 어떤 자산으로 얼마의 수수료를 냈는지 알 수 있습니다. 마지막으로, 통계 분석을 수행합니다.
총 거래 횟수, 매수/매도 비율, 평균 체결가 등을 계산하여 트레이딩 패턴을 파악합니다. 예를 들어 평균 매도가가 평균 매수가보다 높으면 수익이 났다고 볼 수 있습니다.
더 정교한 분석을 위해서는 각 매수-매도 쌍을 매칭하여 개별 거래의 손익을 계산하는 로직을 추가할 수 있습니다. 여러분이 이 코드를 사용하면 트레이딩 봇의 성과를 정량적으로 평가할 수 있고, 어떤 전략이 잘 작동하는지 데이터로 확인할 수 있습니다.
또한 모든 거래를 데이터베이스에 저장하여 감사 추적(audit trail)을 구축하고, 세금 신고나 규제 대응에 필요한 증빙 자료를 확보할 수 있습니다.
실전 팁
💡 거래 내역은 최대 1000개까지만 조회되므로, 장기간 데이터는 startTime을 반복해서 변경하며 여러 번 호출해야 합니다.
💡 tradeId는 고유하므로 데이터베이스에 저장할 때 primary key로 사용하면 중복 저장을 방지할 수 있습니다.
💡 수수료는 거래마다 다를 수 있습니다. BNB로 지불하면 25% 할인, VIP 레벨에 따라 추가 할인이 있으므로 정확한 계산을 위해 실제 수수료 데이터를 사용하세요.
💡 isMaker 필드를 확인하면 메이커 주문인지 테이커 주문인지 알 수 있습니다. 메이커는 수수료가 더 저렴하거나 리베이트를 받습니다.
💡 정기적으로 거래 내역을 백업하세요. 거래소에 문제가 생기거나 계정이 해킹되면 데이터를 잃을 수 있으므로 로컬에 저장하는 것이 안전합니다.
8. 웹소켓으로 실시간 시세 스트리밍
시작하며
여러분이 트레이딩 봇을 만들면서 1초마다 API를 호출하여 가격을 확인하고 있다면, 비효율적일 뿐만 아니라 API 제한에 걸릴 위험이 있습니다. 진짜 실시간 트레이딩을 하려면 더 나은 방법이 필요합니다.
이런 문제는 실제 개발 현장에서 자주 발생합니다. REST API는 요청-응답 방식이라 지속적인 폴링(polling)이 필요하고, 이는 불필요한 네트워크 부하와 지연을 발생시킵니다.
특히 초단위 트레이딩에서는 몇 밀리초의 지연도 수익률에 큰 영향을 줍니다. 바로 이럴 때 필요한 것이 웹소켓(WebSocket) 스트리밍입니다.
한 번 연결하면 서버가 가격 변동이 있을 때마다 자동으로 데이터를 푸시해주므로, 실시간성과 효율성을 모두 얻을 수 있습니다.
개요
간단히 말해서, 웹소켓은 서버와 클라이언트 간의 양방향 통신 채널입니다. 일반 HTTP 요청과 달리 연결을 유지한 채로 실시간으로 데이터를 주고받을 수 있어, 실시간 가격 피드에 최적화되어 있습니다.
바이낸스는 다양한 웹소켓 스트림을 제공합니다. 개별 심볼의 실시간 가격, 호가창 업데이트, 체결 내역, 캔들스틱 업데이트 등을 구독할 수 있습니다.
python-binance 라이브러리의 BinanceSocketManager를 사용하면 복잡한 웹소켓 프로토콜을 간단한 콜백 함수로 처리할 수 있습니다. 기존에는 while 루프로 계속 API를 호출했다면, 이제는 웹소켓으로 연결하고 데이터가 도착할 때마다 자동으로 호출되는 콜백 함수에서 처리합니다.
이 방식은 API 제한도 거의 소모하지 않고, 지연도 최소화됩니다. 웹소켓은 연결이 끊길 수 있으므로 재연결 로직이 필요합니다.
네트워크 문제나 서버 점검 등으로 연결이 끊기면 자동으로 재연결하여 데이터 손실을 방지해야 합니다.
코드 예제
from binance import AsyncClient, BinanceSocketManager
import asyncio
import os
from dotenv import load_dotenv
load_dotenv()
# 웹소켓 메시지 처리 콜백 함수
async def handle_socket_message(msg):
"""실시간 가격 업데이트를 처리하는 함수"""
if msg['e'] == 'error':
print(f"에러 발생: {msg['m']}")
else:
# 심볼 티커 정보 처리
symbol = msg['s']
price = msg['c'] # 현재가
change_percent = msg['P'] # 24시간 변동률
volume = msg['v'] # 24시간 거래량
print(f"{symbol} | 가격: ${price} | 변동: {change_percent}% | 거래량: {volume}")
# 여기서 트레이딩 로직 실행 가능
# 예: 가격이 특정 조건을 만족하면 주문 생성
async def start_websocket():
"""웹소켓 연결 시작"""
# 비동기 클라이언트 생성
client = await AsyncClient.create(
os.getenv('BINANCE_API_KEY'),
os.getenv('BINANCE_SECRET_KEY')
)
bm = BinanceSocketManager(client)
# BTC/USDT 심볼 티커 스트림 구독
socket = bm.symbol_ticker_socket('BTCUSDT')
print("웹소켓 연결 시작... (Ctrl+C로 종료)")
# 메시지 수신 대기
async with socket as stream:
while True:
msg = await stream.recv()
await handle_socket_message(msg)
await client.close_connection()
# 이벤트 루프 실행
if __name__ == "__main__":
asyncio.run(start_websocket())
설명
이것이 하는 일: 웹소켓 연결을 통해 실시간으로 가격 변동을 스트리밍하여 지연 없이 트레이딩 신호를 생성하고 즉각적으로 대응합니다. 첫 번째로, 비동기 프로그래밍을 사용합니다.
웹소켓은 본질적으로 비동기 방식이므로 asyncio와 AsyncClient를 사용해야 합니다. async def로 정의된 함수는 코루틴이며, await 키워드로 비동기 작업을 기다립니다.
이 방식은 단일 스레드에서도 여러 웹소켓 연결을 동시에 처리할 수 있어 효율적입니다. 그 다음으로, BinanceSocketManager가 웹소켓 연결을 관리합니다.
symbol_ticker_socket('BTCUSDT')는 BTC/USDT의 24시간 통계를 실시간으로 스트리밍하는 소켓을 생성합니다. 다른 스트림으로는 trade_socket()(실시간 체결), kline_socket()(실시간 캔들), depth_socket()(호가창 업데이트) 등이 있습니다.
async with 구문으로 소켓을 컨텍스트 매니저로 사용하면 연결 해제도 자동으로 처리됩니다. 마지막으로, 무한 루프 안에서 stream.recv()로 메시지를 기다립니다.
새 데이터가 도착하면 즉시 handle_socket_message() 함수가 호출되어 처리합니다. 메시지는 JSON 형태의 딕셔너리로, 현재가('c'), 변동률('P'), 거래량('v') 등의 필드를 포함합니다.
이 데이터를 기반으로 즉시 매매 판단을 내릴 수 있으며, REST API처럼 지연이 없습니다. 여러분이 이 코드를 사용하면 밀리초 단위의 실시간 트레이딩이 가능하고, API 제한 걱정 없이 여러 코인을 동시에 모니터링할 수 있습니다.
또한 네트워크 효율성이 높아 서버 비용을 절감하고, 가격 변동에 즉각 반응하여 더 나은 진입/청산 타이밍을 잡을 수 있습니다.
실전 팁
💡 여러 코인을 동시에 모니터링하려면 multiplex_socket()을 사용하세요. 하나의 연결로 여러 스트림을 구독할 수 있어 효율적입니다.
💡 웹소켓은 가끔 연결이 끊깁니다. try-except 블록과 재연결 로직을 추가하여 안정성을 높이세요. 10초 이상 메시지가 없으면 재연결하는 것이 좋습니다.
💡 콜백 함수에서 무거운 작업(DB 저장, 복잡한 계산)을 하면 메시지 처리가 밀립니다. 별도의 큐나 스레드로 분리하세요.
💡 웹소켓 데이터는 REST API보다 1-2초 빠를 수 있습니다. 초단타 트레이딩에서는 이 차이가 수익률을 좌우합니다.
💡 개발 중에는 print(msg)로 전체 메시지 구조를 확인하세요. 문서에 없는 유용한 필드들이 포함되어 있을 수 있습니다.
9. 에러 처리 및 재시도 로직 구현
시작하며
여러분이 트레이딩 봇을 24시간 운영하다 보면 언젠가는 에러가 발생합니다. 네트워크가 끊기거나, API 서버가 점검 중이거나, 주문이 거부될 수 있습니다.
이때 봇이 그냥 멈춰버린다면 큰 손실로 이어질 수 있습니다. 이런 문제는 실제 개발 현장에서 자주 발생합니다.
에러 처리 없이 만든 봇은 첫 번째 예외가 발생하는 순간 크래시되어, 중요한 거래 기회를 놓치거나 포지션을 방치하게 됩니다. 특히 실전 환경에서는 예상치 못한 다양한 에러가 발생하므로 견고한 예외 처리가 필수입니다.
바로 이럴 때 필요한 것이 체계적인 에러 처리와 재시도 로직입니다. 일시적인 문제는 자동으로 재시도하고, 심각한 문제는 로그를 남기고 안전하게 종료하는 방어적 프로그래밍이 중요합니다.
개요
간단히 말해서, 에러 처리는 예상 가능한 모든 실패 시나리오를 미리 고려하여 봇이 안전하게 대응하도록 만드는 것입니다. 네트워크 오류, API 제한 초과, 잔고 부족, 주문 거부 등 각 상황마다 적절한 대응이 필요합니다.
바이낸스 API는 다양한 예외를 발생시킵니다. BinanceAPIException은 API 레벨의 에러(잔고 부족, 주문 크기 오류 등), BinanceRequestException은 네트워크 오류, BinanceOrderException은 주문 관련 에러를 나타냅니다.
각 예외를 적절히 잡아서 처리해야 봇이 안정적으로 동작합니다. 기존에는 에러가 발생하면 프로그램이 종료되었다면, 이제는 try-except 블록, 재시도 로직, 백오프 전략을 사용하여 일시적인 문제를 자동으로 복구합니다.
예를 들어 네트워크 오류 시 5초 후 재시도, 3번 실패 시 알림 발송 같은 방식입니다. 에러 로깅도 중요합니다.
모든 예외를 파일이나 외부 서비스에 기록하여 나중에 분석하고 개선할 수 있어야 합니다. 또한 심각한 에러 발생 시 텔레그램이나 이메일로 즉시 알림을 받아 빠르게 대응할 수 있습니다.
코드 예제
from binance.client import Client
from binance.exceptions import BinanceAPIException, BinanceRequestException
import time
import logging
import os
from dotenv import load_dotenv
load_dotenv()
# 로깅 설정
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('trading_bot.log'), # 파일에 저장
logging.StreamHandler() # 콘솔에도 출력
]
)
logger = logging.getLogger(__name__)
client = Client(os.getenv('BINANCE_API_KEY'), os.getenv('BINANCE_SECRET_KEY'))
def retry_with_backoff(func, max_retries=3, initial_delay=1):
"""재시도 로직을 구현한 데코레이터 함수"""
for attempt in range(max_retries):
try:
return func()
except BinanceRequestException as e:
# 네트워크 오류 - 재시도 가능
delay = initial_delay * (2 ** attempt) # 지수 백오프
logger.warning(f"네트워크 오류 발생 (시도 {attempt + 1}/{max_retries}). {delay}초 후 재시도...")
time.sleep(delay)
except BinanceAPIException as e:
# API 에러 - 에러 코드에 따라 처리
if e.code == -1021: # 타임스탬프 오류
logger.error("시스템 시간이 동기화되지 않음")
raise
elif e.code == -2010: # 잔고 부족
logger.error(f"잔고 부족: {e.message}")
raise
elif e.code == -1003: # 요청 제한 초과
logger.warning("API 요청 제한 초과. 60초 대기...")
time.sleep(60)
else:
logger.error(f"API 에러 [{e.code}]: {e.message}")
raise
except Exception as e:
# 기타 예외
logger.error(f"예상치 못한 에러: {str(e)}")
raise
logger.error(f"{max_retries}번 재시도 실패")
raise Exception("최대 재시도 횟수 초과")
# 사용 예시
def place_order_safely():
"""안전한 주문 실행"""
def order_func():
return client.order_market_buy(symbol='BTCUSDT', quantity=0.001)
try:
order = retry_with_backoff(order_func)
logger.info(f"주문 성공: {order['orderId']}")
return order
except Exception as e:
logger.error(f"주문 최종 실패: {e}")
# 여기서 텔레그램 알림 등 추가 액션
return None
설명
이것이 하는 일: 다양한 에러 상황을 예측하고 적절히 처리하여 트레이딩 봇이 24시간 안정적으로 운영되도록 합니다. 첫 번째로, 로깅 시스템을 구축합니다.
logging 모듈은 Python의 표준 로깅 도구로, 심각도 수준(DEBUG, INFO, WARNING, ERROR, CRITICAL)별로 메시지를 기록합니다. FileHandler로 파일에 저장하고 StreamHandler로 콘솔에도 출력하여 실시간 모니터링과 사후 분석을 모두 가능하게 합니다.
로그 파일은 날짜별로 로테이션하여 관리하는 것이 좋습니다. 그 다음으로, 재시도 로직을 구현합니다.
retry_with_backoff() 함수는 최대 재시도 횟수까지 함수 실행을 반복하며, 실패할 때마다 지수적으로 증가하는 대기 시간(1초 → 2초 → 4초)을 적용합니다. 이를 지수 백오프(Exponential Backoff)라고 하며, 서버 부하를 줄이고 일시적인 문제가 해결될 시간을 줍니다.
바이낸스 같은 대형 서비스는 이런 재시도 패턴을 권장합니다. 마지막으로, 예외 종류별로 다른 대응을 합니다.
BinanceRequestException은 네트워크 오류로 재시도 가능, BinanceAPIException은 에러 코드를 확인하여 처리합니다. 예를 들어 -2010(잔고 부족)은 재시도해도 소용없으므로 즉시 예외를 발생시키고, -1003(요청 제한 초과)은 60초 대기 후 계속 진행합니다.
이렇게 상황별로 맞춤 대응하면 복구율이 크게 높아집니다. 여러분이 이 코드를 사용하면 일시적인 네트워크 문제나 API 장애에도 봇이 자동으로 복구되어 가동 중단 시간을 최소화할 수 있습니다.
또한 모든 에러가 로그로 기록되어 패턴을 분석하고 코드를 개선할 수 있으며, 심각한 문제 발생 시 즉시 알림을 받아 대응할 수 있습니다.
실전 팁
💡 에러 코드는 바이낸스 공식 문서에서 확인하세요. 각 코드마다 의미와 권장 대응 방법이 나와 있습니다.
💡 재시도 횟수는 상황에 따라 조절하세요. 네트워크 오류는 5-10번, API 에러는 2-3번 정도가 적당합니다.
💡 로그 파일이 너무 커지지 않도록 RotatingFileHandler를 사용하세요. 10MB마다 새 파일로 로테이션하고 최근 5개만 유지하는 식입니다.
💡 심각한 에러 시 Slack이나 Telegram으로 알림을 보내세요. 로그만으로는 문제를 즉시 인지하기 어렵습니다.
💡 예외 처리 후에도 봇의 상태를 점검하세요. 예를 들어 주문 실패 후 잔고가 예상과 다르다면 재동기화가 필요할 수 있습니다.
10. 환경별 설정 관리 (테스트넷 vs 실전)
시작하며
여러분이 개발한 트레이딩 봇을 바로 실전에 투입하면 어떻게 될까요? 코드에 버그가 있다면 실제 자산을 잃을 수 있습니다.
안전하게 테스트할 방법이 필요합니다. 이런 문제는 실제 개발 현장에서 자주 발생합니다.
개발 환경과 프로덕션 환경을 제대로 분리하지 않으면 테스트 코드가 실전 계좌에서 실행되거나, 반대로 실전 코드가 테스트 환경에 접근하여 혼란을 초래합니다. 특히 금융 거래에서는 한 번의 실수가 큰 손실로 이어집니다.
바로 이럴 때 필요한 것이 환경별 설정 관리입니다. 테스트넷과 메인넷을 명확히 구분하고, 환경 변수로 쉽게 전환할 수 있도록 구조화하는 것이 중요합니다.
개요
간단히 말해서, 환경 관리는 개발, 테스트, 프로덕션 환경을 분리하여 각각 다른 설정을 사용하도록 하는 것입니다. 바이낸스는 실제 거래가 일어나지 않는 테스트넷을 제공하여 안전하게 봇을 검증할 수 있습니다.
테스트넷은 메인넷과 동일한 API를 제공하지만 가상의 자산을 사용합니다. 주문을 생성하고 체결되는 과정을 실제처럼 경험할 수 있지만, 실제 돈은 오가지 않습니다.
따라서 새로운 전략을 개발하거나 코드를 수정할 때는 반드시 테스트넷에서 먼저 검증해야 합니다. 기존에는 코드에 환경 설정을 하드코딩했다면, 이제는 .env 파일과 환경 변수를 사용하여 유연하게 관리합니다.
ENVIRONMENT=test 또는 ENVIRONMENT=production 같은 변수 하나로 전체 봇의 동작 모드를 전환할 수 있습니다. 프로덕션 환경으로 전환할 때는 체크리스트를 만드는 것이 좋습니다.
API 키 확인, 주문 크기 제한 설정, 로깅 레벨 조정, 알림 설정 등을 점검하여 실수를 방지합니다.
코드 예제
import os
from dotenv import load_dotenv
from binance.client import Client
import logging
load_dotenv()
# 환경 설정
ENVIRONMENT = os.getenv('ENVIRONMENT', 'test') # 기본값은 test
IS_PRODUCTION = ENVIRONMENT == 'production'
# 환경별 설정 클래스
class Config:
def __init__(self, env='test'):
self.env = env
self.is_production = (env == 'production')
# 환경별 API 키 (테스트넷과 메인넷은 다른 키 사용)
if self.is_production:
self.api_key = os.getenv('BINANCE_API_KEY')
self.secret_key = os.getenv('BINANCE_SECRET_KEY')
self.testnet = False
else:
self.api_key = os.getenv('BINANCE_TESTNET_API_KEY')
self.secret_key = os.getenv('BINANCE_TESTNET_SECRET_KEY')
self.testnet = True
# 환경별 주문 크기 제한
self.max_order_size = 0.01 if self.is_production else 1.0 # 실전에서는 작게
# 로깅 레벨
self.log_level = logging.INFO if self.is_production else logging.DEBUG
# 설정 로드
config = Config(ENVIRONMENT)
# 클라이언트 생성
client = Client(
config.api_key,
config.secret_key,
testnet=config.testnet
)
# 환경 확인
print(f"🚀 봇 시작 - 환경: {'🔴 PRODUCTION' if config.is_production else '🟢 TEST'}")
print(f"테스트넷 모드: {config.testnet}")
print(f"최대 주문 크기: {config.max_order_size} BTC")
# 안전장치: 프로덕션에서는 추가 확인
if config.is_production:
confirm = input("⚠️ 프로덕션 환경입니다. 실제 자산이 거래됩니다. 계속하시겠습니까? (yes/no): ")
if confirm.lower() != 'yes':
print("봇 종료됨")
exit()
# 주문 함수 예시
def place_order(symbol, quantity):
# 주문 크기 제한 체크
if quantity > config.max_order_size:
print(f"⚠️ 주문 크기({quantity})가 제한({config.max_order_size})을 초과하여 조정됨")
quantity = config.max_order_size
try:
order = client.order_market_buy(symbol=symbol, quantity=quantity)
print(f"✅ 주문 성공: {order['orderId']}")
except Exception as e:
print(f"❌ 주문 실패: {e}")
설명
이것이 하는 일: 개발/테스트/프로덕션 환경을 명확히 분리하고, 각 환경에 맞는 설정을 자동으로 적용하여 안전하게 봇을 운영합니다. 첫 번째로, Config 클래스로 환경별 설정을 중앙화합니다.
환경 변수 ENVIRONMENT의 값에 따라 API 키, 주문 크기 제한, 로깅 레벨 등이 자동으로 결정됩니다. 이렇게 하면 코드 한 줄도 수정하지 않고 .env 파일만 바꿔서 환경을 전환할 수 있습니다.
테스트넷과 메인넷의 API 키는 완전히 다르므로 반드시 분리하여 관리해야 합니다. 그 다음으로, 프로덕션 환경에서는 추가 안전장치를 적용합니다.
최대 주문 크기를 제한하여 실수로 큰 금액을 거래하는 것을 방지하고, 봇 시작 시 사용자에게 명시적인 확인을 요구합니다. 또한 로깅 레벨을 INFO로 설정하여 디버그 메시지로 로그가 넘치는 것을 방지합니다.
이런 작은 안전장치들이 치명적인 실수를 막아줍니다. 마지막으로, 모든 주문 함수에서 환경별 제한을 체크합니다.
place_order() 함수는 요청된 수량이 설정된 최대값을 초과하면 자동으로 조정합니다. 이렇게 하면 전략 코드에서 실수로 큰 수량을 주문해도 실제 실행 시 제한됩니다.
테스트 환경에서는 이 제한을 크게 설정하여 자유롭게 테스트할 수 있습니다. 여러분이 이 코드를 사용하면 개발 단계에서는 자유롭게 실험하고, 프로덕션에서는 안전장치로 보호받을 수 있습니다.
또한 환경 전환이 매우 간단해져서 테스트 → 프로덕션 배포가 빠르고 안전하게 이루어지며, 팀원들과 협업할 때도 각자 다른 환경에서 작업할 수 있습니다.
실전 팁
💡 .env.example 파일을 만들어서 필요한 환경 변수 목록을 문서화하세요. 새 팀원이 쉽게 설정할 수 있습니다.
💡 테스트넷에서 최소 1-2주는 봇을 돌려보세요. 다양한 시장 상황(급등, 급락, 횡보)에서의 동작을 모두 확인해야 합니다.
💡 프로덕션 API 키는 IP 화이트리스트를 설정하고, 출금 권한은 절대 주지 마세요. 거래 권한만으로도 충분합니다.
💡 환경별로 다른 데이터베이스나 로그 파일을 사용하세요. 테스트 데이터와 실전 데이터가 섞이면 분석이 어렵습니다.
💡 프로덕션 배포 전 체크리스트를 만드세요: API 키 확인, 주문 크기 제한 확인, 알림 설정 확인, 비상 정지 버튼 테스트 등.