Hyperliquid 실시간 오더북 WebSocket 구독 가이드
-
hyperliquid orderbook websocket
-
real time hyperliquid api
-
hyperliquid 실시간 오더북
-
hyperliquid websocket 구독
-
hyperliquid l2 book
오더북은 거래소의 핵심 데이터 구조입니다. 시장에 아직 체결되지 않은 매수·매도 지정가 주문이 어떤 가격대에 얼마나 쌓여 있는지를 실시간으로 보여줍니다. 퀀트 트레이딩 봇, 차익거래 시스템, 시세 분석 대시보드처럼 밀리초 단위의 최신 호가 정보가 중요한 환경에서는 오더북 데이터를 얼마나 안정적으로 받느냐가 전략 성과에 직접적인 영향을 줄 수 있어요.
Hyperliquid는 WebSocket 인터페이스를 제공해 L2 오더북의 스냅샷과 증분 업데이트를 실시간으로 구독할 수 있게 합니다. 이 글에서는 Hyperliquid 오더북 WebSocket 구독 방식, 메시지 포맷, 로컬 오더북 유지 방법, 재연결 처리 등 실전 개발에 필요한 내용을 정리합니다. 반대로 직접 인프라를 구축하고 싶지 않은 트레이더라면, OneKey Perps에서 실시간 시세와 거래 기능을 바로 사용할 수 있습니다.
WebSocket과 REST 폴링 비교
기술 구현에 들어가기 전에, 왜 REST API를 주기적으로 호출하는 방식보다 WebSocket이 실시간 오더북에 더 적합한지 이해할 필요가 있습니다.
REST 폴링은 일정 간격으로 서버에 요청을 보내 최신 데이터를 가져오는 방식입니다. 구현은 단순하지만 요청 간격 사이의 변화를 놓칠 수 있고, 짧은 간격으로 호출하면 지연과 API 부담이 커집니다. 반면 WebSocket은 연결을 유지한 상태에서 서버가 변경 사항을 즉시 푸시하므로, 실시간 오더북처럼 빠르게 변하는 데이터에 더 적합합니다.
실시간 오더북이 필요한 경우 REST 폴링은 WebSocket을 대체하기 어렵습니다. 전체 WebSocket 인터페이스 규격은 Hyperliquid 공식 문서를 기준으로 확인하는 것이 좋습니다.
WebSocket 연결 설정
엔드포인트 주소
Hyperliquid의 WebSocket 엔드포인트는 다음과 같습니다. 실제 운영 시점의 최신 정보는 공식 문서를 기준으로 확인하세요.
wss://api.hyperliquid.xyz/ws
기본 연결 코드
import asyncio
import json
import websockets
WS_URL = "wss://api.hyperliquid.xyz/ws"
async def connect_and_subscribe():
async with websockets.connect(WS_URL) as ws:
# 구독 메시지 전송
subscribe_msg = {
"method": "subscribe",
"subscription": {
"type": "l2Book",
"coin": "BTC"
}
}
await ws.send(json.dumps(subscribe_msg))
# 메시지 지속 수신
async for raw_msg in ws:
msg = json.loads(raw_msg)
handle_message(msg)
asyncio.run(connect_and_subscribe())
구독 메시지 형식
L2 오더북 구독
BTC의 L2 오더북을 구독하는 메시지는 다음과 같습니다.
{
"method": "subscribe",
"subscription": {
"type": "l2Book",
"coin": "BTC"
}
}
Hyperliquid는 여러 거래쌍을 동시에 구독할 수 있습니다. 각 코인에 대해 구독 메시지를 반복해서 보내면, 거래쌍별로 독립적인 업데이트가 푸시됩니다.
구독 취소
{
"method": "unsubscribe",
"subscription": {
"type": "l2Book",
"coin": "BTC"
}
}
메시지 타입: 스냅샷과 증분 업데이트
초기 스냅샷
구독 후 서버는 먼저 전체 오더북 스냅샷을 전송합니다. 여기에는 현재 각 가격대에 쌓여 있는 주문 수량이 포함됩니다.
{
"channel": "l2Book",
"data": {
"coin": "BTC",
"time": 1714500000000,
"levels": [
[
[{"px": "64000.0", "sz": "0.5", "n": 3}],
[{"px": "64010.0", "sz": "0.3", "n": 2}]
]
]
}
}
px: 가격sz: 해당 가격대의 총 주문 수량입니다. 0이면 해당 가격대가 비었다는 의미입니다.n: 해당 가격대에 있는 주문 개수입니다.
위 예시에서 bids, 즉 매수 호가는 높은 가격에서 낮은 가격 순으로 정렬되고, asks, 즉 매도 호가는 낮은 가격에서 높은 가격 순으로 정렬됩니다.
증분 업데이트
초기 스냅샷 이후에는 변경된 가격대만 증분 업데이트로 전송됩니다.
{
"channel": "l2Book",
"data": {
"coin": "BTC",
"time": 1714500000100,
"levels": [
[
[{"px": "63990.0", "sz": "0.0", "n": 0}],
[{"px": "64010.0", "sz": "0.8", "n": 4}]
]
]
}
}
sz가 "0", "0.0" 또는 숫자 0이면 해당 가격대의 주문이 모두 체결되었거나 취소되었다는 뜻입니다. 이 경우 로컬 오더북에서 해당 가격대를 삭제해야 합니다.
로컬 오더북 상태 유지하기
WebSocket 데이터를 사용할 때 가장 중요한 부분은 로컬 오더북을 올바르게 유지하는 것입니다.
from sortedcontainers import SortedDict
class LocalOrderBook:
def __init__(self, coin):
self.coin = coin
self.bids = SortedDict(lambda x: -float(x)) # 매수 호가: 높은 가격에서 낮은 가격 순
self.asks = SortedDict(lambda x: float(x)) # 매도 호가: 낮은 가격에서 높은 가격 순
self.last_time = None
def apply_snapshot(self, data):
self.bids.clear()
self.asks.clear()
levels = data["levels"][0]
for bid in levels[0]:
if float(bid["sz"]) > 0:
self.bids[bid["px"]] = float(bid["sz"])
for ask in levels[1]:
if float(ask["sz"]) > 0:
self.asks[ask["px"]] = float(ask["sz"])
self.last_time = data["time"]
def apply_delta(self, data):
levels = data["levels"][0]
for bid in levels[0]:
px, sz = bid["px"], float(bid["sz"])
if sz == 0:
self.bids.pop(px, None)
else:
self.bids[px] = sz
for ask in levels[1]:
px, sz = ask["px"], float(ask["sz"])
if sz == 0:
self.asks.pop(px, None)
else:
self.asks[px] = sz
self.last_time = data["time"]
@property
def best_bid(self):
return next(iter(self.bids.items()), None)
@property
def best_ask(self):
return next(iter(self.asks.items()), None)
@property
def mid_price(self):
bid = self.best_bid
ask = self.best_ask
if bid and ask:
return (float(bid[0]) + float(ask[0])) / 2
return None
@property
def spread(self):
bid = self.best_bid
ask = self.best_ask
if bid and ask:
return float(ask[0]) - float(bid[0])
return None
메시지 처리 로직
스냅샷 메시지와 증분 메시지를 구분해 로컬 상태에 반영합니다.
order_book = LocalOrderBook("BTC")
is_initialized = False
def handle_message(msg):
global is_initialized
if msg.get("channel") != "l2Book":
return
data = msg["data"]
if not is_initialized:
order_book.apply_snapshot(data)
is_initialized = True
print(f"오더북 초기화 완료: 매수 {len(order_book.bids)}개 가격대, 매도 {len(order_book.asks)}개 가격대")
else:
order_book.apply_delta(data)
# 현재 호가 정보 출력
print(f"최우선 매수호가: {order_book.best_bid}, 최우선 매도호가: {order_book.best_ask}, 스프레드: {order_book.spread}")
연결 끊김과 재연결 처리
WebSocket 연결은 네트워크 변동, 서버 측 연결 종료, 클라이언트 환경 문제 등으로 끊길 수 있습니다. 운영 환경에서는 자동 재연결 로직이 필수입니다.
import asyncio
import json
import websockets
async def subscribe_with_reconnect(coin, handle_fn, max_retries=None):
retries = 0
while max_retries is None or retries < max_retries:
try:
async with websockets.connect(WS_URL, ping_interval=20, ping_timeout=10) as ws:
retries = 0 # 연결 성공 시 재시도 횟수 초기화
is_initialized = False
await ws.send(json.dumps({
"method": "subscribe",
"subscription": {"type": "l2Book", "coin": coin}
}))
async for raw_msg in ws:
handle_fn(json.loads(raw_msg))
except (websockets.ConnectionClosed, ConnectionError, OSError) as e:
retries += 1
wait = min(2 ** retries, 60) # 지수 백오프, 최대 60초 대기
print(f"연결 끊김({e}), {wait}초 후 재시도합니다. 재시도 횟수: {retries}")
is_initialized = False # 다음 연결에서는 다시 스냅샷을 받아야 함
await asyncio.sleep(wait)
재연결할 때는 반드시 is_initialized 상태를 초기화해야 합니다. 연결이 다시 열리면 서버는 중단된 지점부터 증분 업데이트를 이어 보내는 것이 아니라, 새 구독에 대해 다시 전체 스냅샷을 전송하기 때문입니다.
시퀀스와 메시지 순서
Hyperliquid WebSocket 메시지에는 타임스탬프 필드가 포함되어 있어 메시지 순서 이상 여부를 감지하는 데 활용할 수 있습니다. 운영 시스템에서는 다음과 같은 처리를 권장합니다.
- 마지막으로 처리한 메시지의 타임스탬프를 기록합니다.
- 새 메시지의 타임스탬프가 이미 처리한 메시지보다 이르면 오래된 메시지로 보고 버립니다.
- 오랫동안 메시지가 들어오지 않으면 ping을 보내거나 연결을 재설정합니다.
- 고빈도 전략에서는 네트워크 지연을 줄이기 위해 전용 회선이나 지리적으로 가까운 노드 사용을 검토할 수 있습니다.
여러 거래쌍 동시 구독
하나의 WebSocket 연결에서 여러 거래쌍을 동시에 구독할 수 있습니다. 비동기 구조로 관리하는 방식이 일반적입니다.
coins = ["BTC", "ETH", "SOL"]
order_books = {coin: LocalOrderBook(coin) for coin in coins}
async def multi_subscribe():
async with websockets.connect(WS_URL) as ws:
for coin in coins:
await ws.send(json.dumps({
"method": "subscribe",
"subscription": {"type": "l2Book", "coin": coin}
}))
async for raw_msg in ws:
msg = json.loads(raw_msg)
if msg.get("channel") == "l2Book":
coin = msg["data"]["coin"]
if coin in order_books:
order_books[coin].apply_delta(msg["data"])
운영 코드에서는 거래쌍별 초기화 여부를 따로 관리하는 편이 안전합니다. 예를 들어 initialized = {coin: False for coin in coins}처럼 코인별 상태를 분리하면, 특정 거래쌍의 스냅샷 처리 여부를 더 정확히 제어할 수 있습니다.
실제 활용 사례
실시간 오더북 데이터는 다음과 같은 곳에서 활용됩니다.
- 트레이딩 봇: 호가 스프레드에 따라 지정가 주문 가격을 동적으로 조정합니다.
- 차익거래 시스템: Hyperliquid와 dYdX, GMX 등 다른 거래소 간 가격 차이를 모니터링합니다.
- 시세 분석 대시보드: 오더북 깊이, 스프레드 변화, 유동성 분포를 시각화합니다.
- 리스크 관리 시스템: 스프레드가 비정상적으로 확대되거나 유동성이 급감할 때 알림을 발생시킵니다.
OneKey Perps: 직접 인프라를 만들지 않는 선택지
WebSocket 기반 오더북 시스템을 직접 구축하려면 연결 관리, 데이터 정합성, 장애 복구, 지연 모니터링 등 상당한 엔지니어링 작업이 필요합니다. 목표가 퀀트 시스템 개발이 아니라 온체인 무기한 선물 거래라면, OneKey Perps를 사용하는 것이 더 현실적인 워크플로가 될 수 있습니다.
OneKey Perps는 실시간 시세 표시와 거래 기능을 제공하며, OneKey 하드웨어 지갑의 보안 서명을 함께 사용할 수 있습니다. 개발자가 아닌 트레이더라면 복잡한 인프라 구축보다 거래 판단과 리스크 관리에 더 집중할 수 있습니다.
OneKey 앱을 설치한 뒤 지갑을 연결하고 OneKey Perps에서 지원되는 마켓을 확인해 보세요. 과도한 기대 수익을 전제로 하기보다, 소액으로 주문 흐름과 수수료, 청산 리스크를 먼저 이해하는 방식이 좋습니다.
WalletConnect를 통해 탈중앙화 애플리케이션에 연결하려는 사용자라면 WalletConnect 문서의 통합 가이드를 참고할 수 있습니다. OneKey는 표준 WalletConnect 프로토콜을 지원하므로 Hyperliquid 같은 Web3 애플리케이션과 보다 안전하게 상호작용할 수 있습니다.
자주 묻는 질문
Q1. WebSocket 연결 수 제한이 있나요?
Hyperliquid의 연결 수 제한은 공식 문서를 기준으로 확인해야 합니다. 일반적으로는 단일 IP에서 너무 많은 동시 연결을 만드는 것을 피하고, 여러 거래쌍을 구독할 때는 여러 연결을 새로 만드는 대신 하나의 연결을 재사용하는 편이 좋습니다.
Q2. 증분 업데이트 메시지를 하나라도 놓치면 오더북 상태가 틀어지나요?
네. 네트워크 문제로 증분 메시지를 놓치면 로컬 오더북 상태가 서버의 실제 상태와 달라질 수 있습니다. 스프레드가 비정상적으로 보이거나 최우선 호가가 갑자기 튀는 등 상태 불일치가 의심되면, 연결을 닫고 다시 구독해 최신 스냅샷을 받는 방식이 안전합니다.
Q3. 실시간 오더북 데이터를 백테스트에 바로 사용할 수 있나요?
실시간 스트림 자체를 곧바로 백테스트에 사용하는 것은 현실적이지 않습니다. 백테스트에는 과거 데이터가 필요하기 때문입니다. 실시간 오더북 데이터를 시간순으로 저장해 tick data를 축적한 뒤, 충분한 히스토리가 쌓이면 연구용 백테스트에 사용할 수 있습니다. 초기 검증 단계에서는 Hyperliquid의 과거 캔들 API와 함께 활용하는 방법도 있습니다.
Q4. WebSocket 연결이 정상인지 어떻게 판단하나요?
다음 지표를 정기적으로 확인하는 것이 좋습니다.
- 마지막 메시지 수신 시각이 설정한 임계값, 예를 들어 5초를 넘지 않는지
- ping/pong 왕복 지연 시간이 합리적인 범위에 있는지
- 특정 거래쌍에서만 메시지가 장시간 들어오지 않는지
- 재연결이 과도하게 반복되고 있지는 않은지
이상 징후가 감지되면 자동으로 재연결 프로세스를 실행하도록 설계하는 것이 좋습니다.
Q5. OneKey 지갑이 거래 지연에 영향을 주나요?
OneKey 하드웨어 지갑의 서명 작업은 일반적으로 수백 밀리초 수준에서 완료되며, 대부분의 일반적인 거래 전략에서는 큰 병목이 되지 않습니다. 다만 초고빈도 전략처럼 초당 수십 번의 주문이 필요한 구조라면 API Agent 서브 계정이나 핫월렛을 고빈도 서명에 사용하고, 주요 자금 관리는 OneKey로 보호하는 식으로 역할을 분리할 수 있습니다.
마무리
실시간 오더북 구독은 고품질 퀀트 트레이딩 시스템의 핵심 데이터 파이프라인입니다. Hyperliquid의 WebSocket API는 구조가 비교적 명확하며, 이 글의 코드 흐름을 바탕으로 스냅샷 처리, 증분 업데이트, 재연결, 다중 거래쌍 구독까지 기본적인 로컬 오더북 시스템을 구성할 수 있습니다.
다만 모든 사용자가 직접 WebSocket 인프라를 만들 필요는 없습니다. 온체인 무기한 선물 거래를 바로 시작하고 싶다면 OneKey 앱을 다운로드해 지갑을 설정하고, OneKey Perps에서 실시간 시세와 거래 기능을 확인해 보세요. 보안 서명과 실사용 중심의 거래 경험을 함께 가져갈 수 있는 실용적인 접근입니다.
리스크 안내: 실시간 오더북 데이터는 특정 시점의 시장 상태를 보여줄 뿐이며, 단독으로 거래 결정을 내리는 근거가 되어서는 안 됩니다. 오더북 기반 자동매매 전략은 급격한 시장 변동, 기술 장애, 전략 설계 오류로 인해 큰 손실을 초래할 수 있습니다. 무기한 선물 거래는 원금 전부를 잃을 수 있는 고위험 거래입니다. 이 글은 기술 참고용이며 투자 조언, 법률 조언 또는 재무 조언이 아닙니다. 충분히 위험을 이해한 뒤 신중하게 판단하시기 바랍니다.



