실시간 금융 데이터 API 개요
금융 시장 데이터(미국 주식, 홍콩 주식, 중국 A주, 외환, 암호화폐, 원자재 등)를 실시간으로 수집하기 위해서는 WebSocket 기반의 스트리밍 API를 연동해야 합니다. 이 가이드에서는 WebSocket 연결 수립, 인증 토큰 처리, 실시간 시세 구독 및 수신 데이터 파싱에 대한 기술적 세부 사항을 다룹니다.
WebSocket 엔드포인트 및 인증
API에 접근하려면 발급받은 인증 토큰을 URL 파라미터로 전달하여 WebSocket 연결을 수립해야 합니다. 자산군에 따라 연결할 엔드포인트가 구분됩니다.
- 외환, 암호화폐, 귀금속:
wss://quote.tradeswitcher.com/quote-b-ws-api?token={YOUR_TOKEN} - 미국 및 홍콩 주식:
wss://quote.tradeswitcher.com/quote-stock-b-ws-api?token={YOUR_TOKEN}
토큰은 서비스 제공자의 공식 포털에서 발급받을 수 있으며, 연결 후 세션 유지를 위해 주기적인 Ping/Pong 또는 하트비트 패킷을 전송해야 합니다.
Python을 이용한 WebSocket 클라이언트 구현
아래는 websocket-client 라이브러리를 사용하여 실시간 주가 및 오더북(Order Book) 데이터를 구독하는 Python 클라이언트 구현 예시입니다. 연결 수명 주기 관리와 하트비트 로직을 포함하도록 구조화되었습니다.
import json
import time
import threading
import websocket
class MarketDataStream:
def __init__(self, endpoint_url, auth_token):
self.ws_url = f"{endpoint_url}?token={auth_token}"
self.socket = None
self.heartbeat_interval = 15 # 하트비트 전송 주기 (초)
def _send_heartbeat(self):
"""서버와의 연결 유지를 위해 주기적으로 하트비트 패킷을 전송합니다."""
while self.socket and self.socket.keep_running:
ping_payload = json.dumps({"cmd_id": 99999, "action": "ping"})
self.socket.send(ping_payload)
time.sleep(self.heartbeat_interval)
def _on_ws_open(self, ws):
print("[INFO] WebSocket 연결이 성공적으로 수립되었습니다.")
# 하트비트 스레드 시작
threading.Thread(target=self._send_heartbeat, daemon=True).start()
# 실시간 체결 및 오더북 데이터 구독 요청
subscription_request = {
"cmd_id": 22002,
"seq_id": 1001,
"trace": "trace-uuid-8f7e6d5c-4b3a",
"data": {
"symbol_list": [
{"code": "AAPL.US", "depth_level": 5},
{"code": "0939.HK", "depth_level": 5}
]
}
}
ws.send(json.dumps(subscription_request))
print("[INFO] 시장 데이터 구독 요청이 전송되었습니다.")
def _on_ws_message(self, ws, payload):
"""서버로부터 수신한 JSON 페이로드를 파싱합니다."""
try:
data = json.loads(payload)
self._process_market_data(data)
except json.JSONDecodeError:
print(f"[ERROR] 잘못된 JSON 형식 수신: {payload}")
def _process_market_data(self, data):
"""수신된 데이터의 명령어 ID(cmd_id)에 따라 적절한 처리를 수행합니다."""
cmd = data.get("cmd_id")
if cmd == 22998:
print(f"[TRADE] {data['data']['code']} - 체결가: {data['data']['price']}")
elif cmd == 22999:
bids = data['data'].get('bids', [])
asks = data['data'].get('asks', [])
print(f"[DEPTH] {data['data']['code']} - 매수호가 1단계: {bids[0]['price'] if bids else 'N/A'}")
elif cmd == 99999:
pass # 하트비트 응답 무시
def _on_ws_error(self, ws, error):
print(f"[ERROR] WebSocket 오류 발생: {error}")
def _on_ws_close(self, ws, close_status_code, close_msg):
print(f"[INFO] WebSocket 연결 종료: 코드={close_status_code}, 메시지={close_msg}")
def connect(self):
self.socket = websocket.WebSocketApp(
self.ws_url,
on_open=self._on_ws_open,
on_message=self._on_ws_message,
on_error=self._on_ws_error,
on_close=self._on_ws_close
)
# 자동 재연결 및 무한 대기 실행
self.socket.run_forever(reconnect=5)
if __name__ == "__main__":
API_ENDPOINT = "wss://quote.tradeswitcher.com/quote-stock-b-ws-api"
TOKEN = "YOUR_ACTUAL_TOKEN_HERE"
stream = MarketDataStream(API_ENDPOINT, TOKEN)
stream.connect()
수신 데이터 페이로드 구조 및 파싱
서버에서 푸시되는 데이터는 주로 최신 체결 정보와 오더북(호가창) 깊이 정보로 나뉩니다. 각 데이터는 cmd_id를 통해 식별됩니다.
1. 최신 체결 데이터 (Trade Tick)
cmd_id: 22998은 최신 시장 체결 내역을 나타냅니다.
{
"cmd_id": 22998,
"data": {
"code": "AAPL.US",
"seq": "1678886400000042",
"tick_time": "1678886400",
"price": "150.25",
"volume": "150",
"turnover": "22537.50",
"trade_direction": 1
}
}
2. 오더북 깊이 데이터 (Order Book Depth)
cmd_id: 22999는 매수(Bids) 및 매도(Asks) 호가창의 깊이 정보를 제공합니다.
{
"cmd_id": 22999,
"data": {
"code": "0939.HK",
"seq": "1678886400000099",
"tick_time": "1678886400",
"bids": [
{"price": "5.10", "volume": "12000"},
{"price": "5.09", "volume": "34000"},
{"price": "5.08", "volume": "55000"},
{"price": "5.07", "volume": "18000"},
{"price": "5.06", "volume": "92000"}
],
"asks": [
{"price": "5.11", "volume": "15000"},
{"price": "5.12", "volume": "28000"},
{"price": "5.13", "volume": "41000"},
{"price": "5.14", "volume": "22000"},
{"price": "5.15", "volume": "85000"}
]
}
}