암호화폐 자동 거래 시스템을 구축하려면 거래소 API의 데이터 포맷을 정확히 이해해야 합니다. 이 튜토리얼에서는 OKX, Binance, Bybit 등 주요 거래소의 REST API와 WebSocket 데이터 구조를 Python으로 파싱하는 방법을 다루겠습니다. 마지막으로 HolySheep AI를 활용하여 시장 데이터 분석 자동화하는 고급 활용법도 소개합니다.
주요 거래소 API 데이터 포맷 비교
각 거래소는 유사하지만 미묘하게 다른 데이터 포맷을 사용합니다. 먼저 전체 구조를 파악한 후 실제 코드 구현으로 넘어가겠습니다.
| 항목 | OKX | Binance | Bybit |
|---|---|---|---|
| 베이스 URL | https://www.okx.com/api/v5 | https://api.binance.com | https://api.bybit.com |
| Ticker 심볼 형식 | BTC-USDT | BTCUSDT | BTCUSDT |
| 호가창 데이터 | bids/asks 배열 | bids/asks 배열 | bids/asks 배열 |
| 시간戳 형식 | Unix ms (밀리초) | Unix ms | Unix ms |
| 인증 방식 | HMAC SHA256 | HMAC SHA256 | HMAC SHA256 |
| 가격 정밀도 | 소수점 2-8자리 | 소수점 2-8자리 | 소수점 2자리 |
Python 개발 환경 설정
필요한 라이브러리를 설치합니다. requests는 REST API, websockets-client는 실시간 데이터 수신, pandas는 데이터 처리에 사용됩니다.
# requirements.txt
requests>=2.28.0
websockets>=10.0
pandas>=1.5.0
aiohttp>=3.8.0
numpy>=1.23.0
python-dotenv>=0.19.0
cryptography>=38.0.0
pip install requests websockets pandas aiohttp numpy python-dotenv cryptography
OKX API 데이터 파싱实战
1. REST API로 현재 시세 조회
import requests
import json
from typing import Dict, List, Optional
class OKXAPIClient:
"""OKX 거래소 API 클라이언트"""
BASE_URL = "https://www.okx.com/api/v5"
def __init__(self, api_key: str = "", secret_key: str = "", passphrase: str = ""):
self.api_key = api_key
self.secret_key = secret_key
self.passphrase = passphrase
self.session = requests.Session()
self.session.headers.update({
"Content-Type": "application/json",
"OK-ACCESS-KEY": api_key,
"OK-ACCESS-PASSPHRASE": passphrase
})
def get_ticker(self, inst_id: str = "BTC-USDT") -> Dict:
"""
단일 거래쌍의 현재 시세 조회
OKX는 심볼 형식으로 BTC-USDT 사용 (하이픈 구분)
"""
endpoint = f"{self.BASE_URL}/market/ticker"
params = {"instId": inst_id}
response = self.session.get(endpoint, params=params)
response.raise_for_status()
data = response.json()
if data.get("code") != "0":
raise ValueError(f"API 오류: {data.get('msg')}")
return self._parse_ticker(data["data"][0])
def _parse_ticker(self, raw_data: Dict) -> Dict:
"""
OKX Ticker 응답 파싱
응답 구조:
{
"instId": "BTC-USDT",
"last": "42150.5",
"lastSz": "0.1",
"askPx": "42150.4",
"askSz": "0.574",
"bidPx": "42150.3",
"bidSz": "2.5",
"open24h": "41500",
"high24h": "42500",
"low24h": "41000",
"volCcy24h": "12345.67",
"vol24h": "12345.67",
"ts": "1703001234567"
}
"""
return {
"symbol": raw_data["instId"],
"price": float(raw_data["last"]),
"volume_24h": float(raw_data["vol24h"]),
"quote_volume_24h": float(raw_data["volCcy24h"]),
"high_24h": float(raw_data["high24h"]),
"low_24h": float(raw_data["low24h"]),
"bid_price": float(raw_data["bidPx"]),
"bid_volume": float(raw_data["bidSz"]),
"ask_price": float(raw_data["askPx"]),
"ask_volume": float(raw_data["askSz"]),
"timestamp_ms": int(raw_data["ts"]),
"raw": raw_data # 원본 데이터 보존
}
def get_orderbook(self, inst_id: str = "BTC-USDT", sz: int = 400) -> Dict:
"""호가창 데이터 조회 (최대 400레벨)"""
endpoint = f"{self.BASE_URL}/market/books"
params = {"instId": inst_id, "sz": sz}
response = self.session.get(endpoint, params=params)
data = response.json()
if data.get("code") != "0":
raise ValueError(f"호가창 조회 실패: {data.get('msg')}")
raw_orderbook = data["data"][0]
return self._parse_orderbook(raw_orderbook)
def _parse_orderbook(self, raw: Dict) -> Dict:
"""
OKX 호가창 파싱
bids: [[가격, 수량, 계약수], ...]
asks: [[가격, 수량, 계약수], ...]
"""
bids = [
{"price": float(b[0]), "volume": float(b[1]), "contracts": int(b[2])}
for b in raw["bids"]
]
asks = [
{"price": float(a[0]), "volume": float(a[1]), "contracts": int(a[2])}
for a in raw["asks"]
]
# 스프레드 계산
best_bid = bids[0]["price"] if bids else 0
best_ask = asks[0]["price"] if asks else 0
spread = best_ask - best_bid
spread_pct = (spread / best_bid * 100) if best_bid > 0 else 0
return {
"symbol": raw["instId"],
"bids": bids,
"asks": asks,
"best_bid": best_bid,
"best_ask": best_ask,
"spread": spread,
"spread_pct": round(spread_pct, 4),
"timestamp_ms": int(raw["ts"]),
"checksum": raw.get("chk")
}
사용 예제
client = OKXAPIClient()
ticker = client.get_ticker("BTC-USDT")
print(f"BTC-USDT 현재가: ${ticker['price']:,.2f}")
print(f"24시간 거래량: {ticker['volume_24h']:,.2f} BTC")
orderbook = client.get_orderbook("BTC-USDT")
print(f"스프레드: {orderbook['spread_pct']:.4f}%")
2. WebSocket 실시간 데이터 수신
import asyncio
import json
import websockets
from typing import Callable, Dict, List
import gzip
class OKXWebSocketClient:
"""OKX WebSocket 실시간 데이터 클라이언트"""
WS_URL = "wss://ws.okx.com:8443/ws/v5/public"
def __init__(self):
self.websocket = None
self.subscriptions: List[Dict] = []
self.callbacks: Dict[str, Callable] = {}
async def connect(self):
"""WebSocket 연결 수립"""
self.websocket = await websockets.connect(self.WS_URL, compression="deflate")
print("OKX WebSocket 연결 성공")
async def subscribe_ticker(self, inst_id: str, callback: Callable[[Dict], None]):
"""티커 실시간 구독"""
subscribe_msg = {
"op": "subscribe",
"args": [{
"channel": "tickers",
"instId": inst_id
}]
}
await self.websocket.send(json.dumps(subscribe_msg))
self.callbacks[f"ticker_{inst_id}"] = callback
print(f"구독 완료: {inst_id} 티커")
async def subscribe_orderbook(self, inst_id: str, callback: Callable[[Dict], None]):
"""호가창 실시간 구독"""
subscribe_msg = {
"op": "subscribe",
"args": [{
"channel": "books-l2-tbt", # 탭 단위 스냅샷 (가장 효율적)
"instId": inst_id,
"sz": "400"
}]
}
await self.websocket.send(json.dumps(subscribe_msg))
self.callbacks[f"orderbook_{inst_id}"] = callback
print(f"구독 완료: {inst_id} 호가창")
async def subscribe_trades(self, inst_id: str, callback: Callable[[Dict], None]):
"""실시간 체결 구독"""
subscribe_msg = {
"op": "subscribe",
"args": [{
"channel": "trades",
"instId": inst_id
}]
}
await self.websocket.send(json.dumps(subscribe_msg))
self.callbacks[f"trades_{inst_id}"] = callback
def _parse_ticker_update(self, data: Dict) -> Dict:
"""실시간 티커 업데이트 파싱"""
raw = data["data"][0]
return {
"symbol": raw["instId"],
"last_price": float(raw["last"]),
"last_volume": float(raw["lastSz"]),
"bid_price": float(raw["bidPx"]),
"bid_volume": float(raw["bidSz"]),
"ask_price": float(raw["askPx"]),
"ask_volume": float(raw["askSz"]),
"high_24h": float(raw["high24h"]),
"low_24h": float(raw["low24h"]),
"volume_24h": float(raw["vol24h"]),
"timestamp_ms": int(raw["ts"])
}
def _parse_orderbook_update(self, data: Dict) -> Dict:
"""호가창 업데이트 파싱 (L2 탭 업데이트)"""
raw = data["data"][0]
# 업데이트 타입: snapshot(0) 또는 update(1)
update_type = raw["action"]
bids = [
{"price": float(b[0]), "volume": float(b[1])}
for b in raw.get("bids", [])
]
asks = [
{"price": float(a[0]), "volume": float(a[1])}
for a in raw.get("asks", [])
]
return {
"symbol": raw["instId"],
"update_type": "snapshot" if update_type == "0" else "update",
"bids": bids,
"asks": asks,
"timestamp_ms": int(raw["ts"]),
"checksum": raw.get("chk")
}
async def listen(self):
"""메시지 리스닝 루프"""
async for message in self.websocket:
# gzip 압축 해제
try:
data = json.loads(gzip.decompress(message))
except:
data = json.loads(message)
# 구독 확인 메시지 처리
if "event" in data:
print(f"구독 이벤트: {data['event']}")
continue
# 데이터 메시지 처리
if "data" in data:
channel = data.get("arg", {}).get("channel", "")
inst_id = data.get("arg", {}).get("instId", "")
if channel == "tickers":
callback_key = f"ticker_{inst_id}"
if callback_key in self.callbacks:
parsed = self._parse_ticker_update(data)
await self.callbacks[callback_key](parsed)
elif "books" in channel:
callback_key = f"orderbook_{inst_id}"
if callback_key in self.callbacks:
parsed = self._parse_orderbook_update(data)
await self.callbacks[callback_key](parsed)
elif channel == "trades":
callback_key = f"trades_{inst_id}"
if callback_key in self.callbacks:
for trade in data["data"]:
parsed = self._parse_trade(trade)
await self.callbacks[callback_key](parsed)
def _parse_trade(self, trade: Dict) -> Dict:
return {
"symbol": trade["instId"],
"trade_id": trade["tradeId"],
"price": float(trade["px"]),
"volume": float(trade["sz"]),
"side": trade["side"], # buy 또는 sell
"timestamp_ms": int(trade["ts"])
}
사용 예제
async def main():
client = OKXWebSocketClient()
await client.connect()
price_history = []
async def on_ticker(ticker: Dict):
price_history.append(ticker["last_price"])
print(f"[{ticker['timestamp_ms']}] BTC: ${ticker['last_price']:,.2f}")
# 1분마다 캔들 형성 (실제 거래 시 로직)
if len(price_history) >= 60:
print(f"1분 봉 형성 완료. 종가: {price_history[-1]}")
await client.subscribe_ticker("BTC-USDT", on_ticker)
try:
await client.listen()
except KeyboardInterrupt:
print("연결 종료")
asyncio.run(main())
Binance API 호환 데이터 파서
크로스 거래소 호환성을 위해 Binance 형식의 통합 데이터 파서를 만들겠습니다. 이 패턴을 활용하면 Bybit 등 다른 거래소로의 확장이 용이합니다.
import requests
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import List, Optional
from datetime import datetime
import pandas as pd
@dataclass
class UnifiedTicker:
"""통일된 티커 데이터 구조"""
exchange: str
symbol: str
price: float
bid_price: float
ask_price: float
bid_volume: float
ask_volume: float
volume_24h: float
high_24h: float
low_24h: float
timestamp_ms: int
@property
def spread(self) -> float:
return self.ask_price - self.bid_price
@property
def spread_pct(self) -> float:
return (self.spread / self.bid_price * 100) if self.bid_price > 0 else 0
@property
def datetime(self) -> datetime:
return datetime.fromtimestamp(self.timestamp_ms / 1000)
@dataclass
class UnifiedOrderbook:
"""통일된 호가창 데이터"""
exchange: str
symbol: str
bids: List[tuple] # [(price, volume), ...]
asks: List[tuple] # [(price, volume), ...]
timestamp_ms: int
@property
def mid_price(self) -> float:
if self.bids and self.asks:
return (self.bids[0][0] + self.asks[0][0]) / 2
return 0
class ExchangeDataProvider(ABC):
"""거래소 데이터 제공자 추상 클래스"""
@abstractmethod
def fetch_ticker(self, symbol: str) -> UnifiedTicker:
pass
@abstractmethod
def fetch_orderbook(self, symbol: str) -> UnifiedOrderbook:
pass
class BinanceProvider(ExchangeDataProvider):
"""Binance 데이터 제공자"""
BASE_URL = "https://api.binance.com"
def _normalize_symbol(self, symbol: str) -> str:
"""BTCUSDT 형식으로 정규화"""
return symbol.upper().replace("-", "").replace("_", "")
def fetch_ticker(self, symbol: str) -> UnifiedTicker:
"""Binance Ticker 조회"""
endpoint = f"{self.BASE_URL}/api/v3/ticker/24hr"
params = {"symbol": self._normalize_symbol(symbol)}
response = requests.get(endpoint, params=params)
data = response.json()
return UnifiedTicker(
exchange="binance",
symbol=symbol,
price=float(data["lastPrice"]),
bid_price=float(data["bidPrice"]),
ask_price=float(data["askPrice"]),
bid_volume=float(data["bidQty"]),
ask_volume=float(data["askQty"]),
volume_24h=float(data["volume"]),
high_24h=float(data["highPrice"]),
low_24h=float(data["lowPrice"]),
timestamp_ms=int(data["closeTime"])
)
def fetch_orderbook(self, symbol: str, limit: int = 20) -> UnifiedOrderbook:
"""Binance 호가창 조회"""
endpoint = f"{self.BASE_URL}/api/v3/depth"
params = {"symbol": self._normalize_symbol(symbol), "limit": limit}
response = requests.get(endpoint, params=params)
data = response.json()
bids = [(float(p), float(v)) for p, v in data["bids"]]
asks = [(float(p), float(v)) for p, v in data["asks"]]
return UnifiedOrderbook(
exchange="binance",
symbol=symbol,
bids=bids,
asks=asks,
timestamp_ms=int(data["lastUpdateId"])
)
class BybitProvider(ExchangeDataProvider):
"""Bybit 데이터 제공자"""
BASE_URL = "https://api.bybit.com"
def _normalize_symbol(self, symbol: str) -> str:
return symbol.upper().replace("-", "").replace("_", "")
def fetch_ticker(self, symbol: str) -> UnifiedTicker:
"""Bybit Ticker 조회"""
endpoint = f"{self.BASE_URL}/v5/market/tickers"
params = {"category": "spot", "symbol": self._normalize_symbol(symbol)}
response = requests.get(endpoint, params=params)
data = response.json()["result"]["list"][0]
return UnifiedTicker(
exchange="bybit",
symbol=symbol,
price=float(data["lastPrice"]),
bid_price=float(data["bid1Price"]),
ask_price=float(data["ask1Price"]),
bid_volume=float(data["bid1Size"]),
ask_volume=float(data["ask1Size"]),
volume_24h=float(data["volume24h"]),
high_24h=float(data["highPrice24h"]),
low_24h=float(data["lowPrice24h"]),
timestamp_ms=int(data["usdIndexPrice"])
)
def fetch_orderbook(self, symbol: str) -> UnifiedOrderbook:
"""Bybit 호가창 조회"""
endpoint = f"{self.BASE_URL}/v5/market/orderbook"
params = {"category": "spot", "symbol": self._normalize_symbol(symbol)}
response = requests.get(endpoint, params=params)
data = response.json()["result"]
bids = [(float(p), float(v)) for p, v in data["b"]]
asks = [(float(p), float(v)) for p, v in data["a"]]
return UnifiedOrderbook(
exchange="bybit",
symbol=symbol,
bids=bids,
asks=asks,
timestamp_ms=int(data["ts"])
)
크로스 거래소 비교
providers = {
"binance": BinanceProvider(),
"bybit": BybitProvider(),
"okx": OKXAPIClient() # 기존 OKX 클라이언트 활용
}
symbols_to_compare = ["BTC-USDT", "ETH-USDT"]
print("=" * 70)
print("크로스 거래소 가격 비교")
print("=" * 70)
for symbol in symbols_to_compare:
print(f"\n📊 {symbol}")
print("-" * 50)
for exchange_name, provider in providers.items():
try:
if hasattr(provider, 'fetch_ticker'):
ticker = provider.fetch_ticker(symbol)
print(f" {exchange_name.upper():10} | ${ticker.price:>12,.2f} | 스프레드: {ticker.spread_pct:.4f}%")
else:
# OKX의 경우 직접 메서드 호출
ticker = provider.get_ticker(symbol)
print(f" {'OKX':10} | ${ticker['price']:>12,.2f} | 스프레드: {ticker['spread'] / ticker['price'] * 100:.4f}%")
except Exception as e:
print(f" {exchange_name.upper():10} | 오류: {str(e)[:30]}")
AI 기반 시장 데이터 분석 자동화
거래소 API 데이터를 수집한 후 HolySheep AI를 활용하면 시장 분석, 감성 분석, 예측 모델링을 자동화할 수 있습니다. HolySheep은 단일 API 키로 GPT-4.1, Claude Sonnet 4.5, Gemini 2.5 Flash, DeepSeek V3.2 등 주요 모델을 지원합니다.
import requests
import json
from typing import List, Dict
class HolySheepAIClient:
"""HolySheep AI API 클라이언트 (OpenAI 호환 인터페이스)"""
BASE_URL = "https://api.holysheep.ai/v1"
def __init__(self, api_key: str):
self.api_key = api_key
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
})
def analyze_market_sentiment(self, ticker_data: List[Dict], model: str = "gpt-4.1") -> Dict:
"""
시장 감성 분석
model 옵션:
- gpt-4.1: $8/MTok (정밀 분석)
- claude-3.5-sonnet: $15/MTok (고품질 분석)
- gemini-2.5-flash: $2.50/MTok (빠른 분석)
- deepseek-v3.2: $0.42/MTok (대량 처리)
"""
prompt = self._build_market_prompt(ticker_data)
payload = {
"model": model,
"messages": [
{
"role": "system",
"content": "당신은 전문 암호화폐 시장 분석가입니다.用户提供された市場データを基に、簡潔で実用的な分析を提供してください。"
},
{
"role": "user",
"content": prompt
}
],
"temperature": 0.3,
"max_tokens": 1000
}
response = self.session.post(
f"{self.BASE_URL}/chat/completions",
json=payload
)
response.raise_for_status()
result = response.json()
return {
"analysis": result["choices"][0]["message"]["content"],
"model": model,
"usage": result.get("usage", {})
}
def _build_market_prompt(self, ticker_data: List[Dict]) -> str:
"""분석 프롬프트 구성"""
ticker_summary = "\n".join([
f"- {t['symbol']}: ${t['price']:,.2f} (24h 볼륨: {t['volume_24h']:,.2f})"
for t in ticker_data
])
return f"""다음 암호화폐 시장 데이터를 분석해주세요:
{ticker_summary}
분석 요청:
1. 전반적 시장 분위기 (강세/약세/중립)
2. 주요 움직임이 있는 코인
3. 투자자 참고 사항
4. 단기 전망 (3시간 기준)
한국어로 답변해주세요."""
def generate_trading_signals(self, price_data: Dict) -> Dict:
"""거래 신호 생성 (DeepSeek V3.2 활용)"""
prompt = f"""
BTC/USDT 시장 데이터:
- 현재가: ${price_data.get('price', 0):,.2f}
- 24시간 고가: ${price_data.get('high_24h', 0):,.2f}
- 24시간 저가: ${price_data.get('low_24h', 0):,.2f}
- 거래량: {price_data.get('volume_24h', 0):,.2f}
위 데이터를 기반으로 간단한 거래 신호를 생성해주세요.
형식: 신호(매수/관망/매도), 이유 1줄, 신뢰도(높음/중간/낮음)
"""
payload = {
"model": "deepseek-v3.2",
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.2,
"max_tokens": 200
}
response = self.session.post(
f"{self.BASE_URL}/chat/completions",
json=payload
)
return response.json()
실제 사용 예제
api_key = "YOUR_HOLYSHEEP_API_KEY" # HolySheep API 키로 교체
ai_client = HolySheepAIClient(api_key)
거래소 데이터 수집
okx_client = OKXAPIClient()
tickers = []
for symbol in ["BTC-USDT", "ETH-USDT", "SOL-USDT"]:
ticker = okx_client.get_ticker(symbol)
tickers.append({
"symbol": symbol,
"price": ticker["price"],
"volume_24h": ticker["volume_24h"],
"high_24h": ticker["high_24h"],
"low_24h": ticker["low_24h"]
})
HolySheep AI로 감성 분석
print("HolySheep AI 시장 감성 분석 중...")
result = ai_client.analyze_market_sentiment(tickers, model="gemini-2.5-flash")
print(f"\n📈 분석 결과:\n{result['analysis']}")
print(f"\n사용 모델: {result['model']}")
월 1,000만 토큰 기준 AI 모델 비용 비교
| 모델 | 가격 ($/MTok) | 월 1천만 토큰 비용 | 상대 비용 | 적합 용도 |
|---|---|---|---|---|
| DeepSeek V3.2 | $0.42 | $4.20 | 基准 (100%) | 대량 데이터 처리, 배치 분석 |
| Gemini 2.5 Flash | $2.50 | $25.00 | 596% | 빠른 실시간 분석 |
| GPT-4.1 | $8.00 | $80.00 | 1,905% | 정밀 분석, 복잡한 추론 |
| Claude Sonnet 4.5 | $15.00 | $150.00 | 3,571% | 최고 품질 요구 분석 |
비용 최적화 전략: 일별 시장 리포트 생성에는 DeepSeek V3.2($0.42), 긴급 알림 분석에는 Gemini 2.5 Flash($2.50), 주간 종합 보고서에는 GPT-4.1($8.00)을 활용하면 비용을 70% 이상 절감할 수 있습니다.
이런 팀에 적합 / 비적합
✅ 적합한 팀
- 암호화폐 트레이딩 봇 개발자: 자동 매매 시스템에 실시간 호가창 데이터 필요
- 포트폴리오 관리 솔루션: 다중 거래소 잔고 및 시세 통합 표시
- 시장 데이터 분석 플랫폼: 크로스 거래소 비교 및 알림 서비스
- 리스크 관리 시스템: 실시간 시세 모니터링 및 자동止损
❌ 비적합한 팀
- 단순 시세 확인만 필요한 사용자: 거래소 앱 사용 권장
- 고빈도 트레이딩 (HFT): REST API 지연 시간으로 한계
- 규제 준수 문제 지역: 해당 거래소 API 사용 제한 확인 필요
가격과 ROI
HolySheep AI를 활용한 자동화 분석 시스템의 ROI를 계산해보겠습니다:
| 시나리오 | 월 처리량 | DeepSeek 비용 | GPT-4.1 비용 | 절감액 |
|---|---|---|---|---|
| 소규모 봇 (일 1,000회 분석) | 30M 토큰 | $12.60 | $240 | $227.40 (95% 절감) |
| 중규모 봇 (일 10,000회 분석) | 300M 토큰 | $126 | $2,400 | $2,274 (95% 절감) |
| 대규모 플랫폼 (일 100,000회) | 3B 토큰 | $1,260 | $24,000 | $22,740 (95% 절감) |
자주 발생하는 오류와 해결책
오류 1: API 응답 파싱 실패 - Invalid JSON
# ❌ 오류 발생 코드
data = response.json()
ticker = {
"price": data["data"][0]["last"] # KeyError: 'last'
}
✅ 해결 코드
data = response.json()
상태 코드 확인
if data.get("code") != "0":
raise ValueError(f"API 오류 코드: {data.get('code')}, 메시지: {data.get('msg')}")
필드 존재 여부 확인
raw = data["data"][0]
ticker = {
"price": float(raw.get("last", raw.get("lastPx", 0))),
"volume": float(raw.get("vol24h", raw.get("vol", 0)))
}
오류 2: WebSocket 연결 끊김 - Pong Timeout
# ❌ 오류 발생 코드
async for message in self.websocket:
# 핑 확인 없이 수신만 처리
process(message)
✅ 해결 코드 - Ping/Pong 핸들링
import asyncio
class RobustWebSocket:
PING_INTERVAL = 20 # 초
async def listen_with_heartbeat(self):
async def send_ping():
while True:
await asyncio.sleep(self.PING_INTERVAL)
if self.websocket and self.websocket.open:
await self.websocket.ping()
ping_task = asyncio.create_task(send_ping())
try:
async for message in self.websocket:
if message.type == websockets.MessageType.pong:
print("Pong 수신 확인")
else:
await self.process_message(message)