시작하기 전에: 실제 발생 오류로 배우는教训
프로덕션 환경에서 OKX WebSocket에 연결할 때 가장 흔히 마주치는 오류들을 먼저 확인합시다:
# 오류 1: ConnectionError: timeout
import websocket
ws = websocket.WebSocketApp("wss://ws.okx.com:8443/ws/v5/public")
결과: ConnectionError: timeout - 방화벽 또는 네트워크 설정 문제
오류 2: 401 Unauthorized (REST API)
import requests
response = requests.get(
"https://www.okx.com/api/v5/market/ticker?instId=BTC-USDT-SWAP",
headers={"OK-ACCESS-KEY": "your_key"}
)
결과: {"code": "501", "msg": "Authentication failed"}
오류 3: WebSocket ping/pong timeout
결과: websocket app died: pong timeout 30.012s
원인: 연결 유지 핑 미발송 또는 서버 응답 지연
본 가이드에서는 이러한 오류들을 선제적으로 방지하면서 OKX永续合约(Perpetual Swap)의 실시간 데이터 스트림을 안정적으로获取하는 방법을 설명합니다.
OKX永续合约 데이터 구조 이해
OKX의永续合约는 USDT-Margined Perpetual과 Coin-Margined Perpetual 두 가지 유형이 있습니다. 대부분의 트레이더는 USDT-Margined Perpetual을 사용합니다:
# OKX永续合约 심볼 구조
BTC-USDT-SWAP # BTC/USDT USDT 마진永续合约
ETH-USDT-SWAP # ETH/USDT USDT 마진永续合约
SOL-USDT-SWAP # SOL/USDT USDT 마진永续合约
주요 데이터 피드 채널
1. trades: 실시간 체결
2. candles: K선/캔들스틱
3. books: 주문서(호가창)
4. mark-price: 마크 프라이스
5. funding: 펀딩レ이트
WebSocket 연결实战
OKX는 wss://ws.okx.com:8443/ws/v5/public 엔드포인트를 제공합니다. 아래는 여러永续合约의 실시간 데이터를 Subscribe하는 완전한 예제입니다:
# okx_perpetual_stream.py
import json
import websocket
import threading
import time
from datetime import datetime
class OKXPerpetualStream:
def __init__(self):
self.ws = None
self.url = "wss://ws.okx.com:8443/ws/v5/public"
self.subscribed = False
def connect(self):
"""WebSocket 연결 및 자동 재연결 포함"""
while True:
try:
self.ws = websocket.WebSocketApp(
self.url,
on_message=self.on_message,
on_error=self.on_error,
on_close=self.on_close,
on_open=self.on_open
)
# 핑Interval 20초 (OKX 권장)
self.ws.run_forever(ping_interval=20, ping_timeout=10)
except Exception as e:
print(f"[{datetime.now()}] 연결 끊김: {e}")
time.sleep(5) # 재연결 전 5초 대기
def on_open(self, ws):
"""연결 성공 시永续合约 Subscribe"""
print(f"[{datetime.now()}] OKX WebSocket 연결됨")
# 여러永续合约의 실시간 데이터 Subscribe
subscribe_msg = {
"op": "subscribe",
"args": [
{
"channel": "tickers",
"instId": "BTC-USDT-SWAP"
},
{
"channel": "candle1m",
"instId": "BTC-USDT-SWAP"
},
{
"channel": "funding",
"instId": "BTC-USDT-SWAP"
},
{
"channel": "trades",
"instId": "BTC-USDT-SWAP"
}
]
}
ws.send(json.dumps(subscribe_msg))
print(f"[{datetime.now()}] 구독 요청 전송 완료")
def on_message(self, ws, message):
"""실시간 메시지 처리"""
data = json.loads(message)
# Subscribe 확인 메시지
if "event" in data:
if data["event"] == "subscribe":
print(f"[{datetime.now()}] 구독 성공: {data.get('arg', {})}")
return
# 실제 데이터 수신
if "data" in data:
arg = data.get("arg", {})
channel = arg.get("channel")
inst_id = arg.get("instId")
for item in data["data"]:
self.process_data(channel, inst_id, item)
def process_data(self, channel, inst_id, item):
"""채널별 데이터 처리"""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
if channel == "tickers":
# 실시간 티커 (가격,成交量,Funding等)
print(f"[{timestamp}] {inst_id} | "
f"Last: {item.get('last', 'N/A')} | "
f"24h Change: {item.get('last', 'N/A')} | "
f"Volume: {item.get('vol24h', 'N/A')}")
elif channel == "candle1m":
# 1분 K선
print(f"[{timestamp}] {inst_id} Candle | "
f"O:{item[1]} H:{item[2]} L:{item[3]} C:{item[4]}")
elif channel == "funding":
# 펀딩レ이트
next_funding_time = datetime.fromtimestamp(
int(item['nextFundingTime']) / 1000
).strftime('%Y-%m-%d %H:%M:%S')
print(f"[{timestamp}] {inst_id} | "
f"Funding Rate: {item['fundingRate']} | "
f"Next: {next_funding_time}")
elif channel == "trades":
# 실시간 체결
side = "매수(BUY)" if item['side'] == 'buy' else "매도(SELL)"
print(f"[{timestamp}] {inst_id} Trade | "
f"Price: {item['px']} | {side} | "
f"Size: {item['sz']}")
def on_error(self, ws, error):
"""오류 처리"""
print(f"[{datetime.now()}] WebSocket 오류: {error}")
def on_close(self, ws, close_status_code, close_msg):
"""연결 종료 처리"""
print(f"[{datetime.now()}] 연결 종료: {close_status_code} - {close_msg}")
def start(self):
"""백그라운드 스레드로 연결 시작"""
thread = threading.Thread(target=self.connect, daemon=True)
thread.start()
return thread
if __name__ == "__main__":
stream = OKXPerpetualStream()
stream_thread = stream.start()
print("OKX永续合约 실시간 데이터 스트림 시작...")
print("종료하려면 Ctrl+C를 누르세요")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n스트림 종료")
exit(0)
# 테스트 실행
$ python okx_perpetual_stream.py
예상 출력:
[2025-01-15 10:30:01.123] OKX WebSocket 연결됨