11. WebSocket Trading
Nhận cập nhật trạng thái lệnh và danh mục tài khoản real-time qua WebSocket
Mục tiêu
Nhận cập nhật tức thời về trạng thái lệnh khớp và danh mục tài khoản qua WebSocket. Cần OTP khi authenticate.
Luồng xử lý
Client ↔ Streaming API ↔ Trading Event- Mở kết nối WebSocket bằng token hợp lệ (cần OTP)
- Subscribe
subscribe_order_status(account_no)vàsubscribe_portfolio(account_no) - Server push event khi trạng thái lệnh hoặc danh mục thay đổi
- Callback
on_tradingphân loại:OrderStatusMessage,OrderMatchMessage,PortfolioMessage - Khi mất kết nối, SDK có cơ chế reconnect
Các loại Message
| Type | Mô tả |
|---|---|
OrderStatusMessage | Trạng thái lệnh (symbol, side, order_id, status, filled_quantity) |
OrderMatchMessage | Một lần khớp lệnh (symbol, side, order_id, match_price, match_qty). Cùng topic với order status, SDK tách thành message riêng |
PortfolioMessage | Vị thế (long_qty, short_qty, net, floating_pl...). Chỉ có với tài khoản phái sinh |
HeartbeatMessage | Keepalive từ server |
PortfolioMessage.total_asset,cash_balancevàstock_valueđã deprecated: server không gửi nên các trường này luôn làNone. Dùng các trường vị thế (long_qty,short_qty,net,floating_pl). Để xem tài sản/sức mua của tài khoản cơ sở, gọi REST (Tutorial 5).Các callback riêng
on_order,on_order_match,on_portfolioxem ở WebSocket nâng cao.
Sample Code — Sync
from ssi_sdk import Auth, Stream, Config
from auth_helper import ensure_auth
from helpers import enum_value
from ssi_sdk.models.streaming import (
OrderStatusMessage, OrderMatchMessage, PortfolioMessage, HeartbeatMessage,
)
config = Config(
client_id="<your_client_id>",
api_key="<your_api_key>",
api_secret="<your_api_secret>",
private_key="<your_private_key>",
)
ACCOUNT_NO = "<your_account_no>"
def on_trading_event(msg):
if isinstance(msg, OrderStatusMessage):
print(
f" [ORDER] {msg.symbol} {enum_value(msg.side)} | ID: {msg.order_id} "
f"| Status: {enum_value(msg.status)} | Khớp: {msg.filled_quantity}/{msg.quantity}"
)
elif isinstance(msg, OrderMatchMessage):
# Khớp lệnh: cùng topic với order status, SDK tách thành OrderMatchMessage
print(
f" [MATCH] {msg.symbol} {enum_value(msg.side)} | ID: {msg.order_id} "
f"| Giá khớp: {msg.match_price} | KL khớp: {msg.match_qty}"
)
elif isinstance(msg, PortfolioMessage):
# Chỉ có với tài khoản phái sinh. total_asset/cash_balance/stock_value đã deprecated
# (server không gửi), dùng các trường vị thế bên dưới.
print(
f" [PORTFOLIO] {msg.symbol} | Long: {msg.long_qty} Short: {msg.short_qty} "
f"Net: {msg.net} | Floating P/L: {msg.floating_pl}"
)
def on_heartbeat(msg: HeartbeatMessage):
print(f" [HEARTBEAT] {msg.status} - {msg.message}")
with Auth(config) as auth:
ensure_auth(auth, otp="<your_otp>") # Cần OTP
with Stream(auth) as stream:
stream.streaming.connect()
stream.streaming.on_trading = on_trading_event
stream.streaming.on_heartbeat = on_heartbeat
# Subscribe trạng thái lệnh
stream.streaming.subscribe_order_status(ACCOUNT_NO)
# Subscribe danh mục (optional, chỉ có event với tài khoản phái sinh)
# stream.streaming.subscribe_portfolio(ACCOUNT_NO)
try:
stream.streaming.wait(timeout_ms=300_000) # 5 phút (mili-giây)
except KeyboardInterrupt:
print("Ngắt kết nối...")Sample Code — Async
import asyncio
from ssi_sdk import AsyncAuth, AsyncStream, Config
from auth_helper import ensure_auth_async
from helpers import enum_value
from ssi_sdk.models.streaming import OrderStatusMessage, OrderMatchMessage, PortfolioMessage
config = Config(...)
ACCOUNT_NO = "<your_account_no>"
def on_trading_event(msg):
if isinstance(msg, OrderStatusMessage):
print(f" [ORDER] {msg.symbol} | {enum_value(msg.status)} | {msg.filled_quantity}/{msg.quantity}")
elif isinstance(msg, OrderMatchMessage):
print(f" [MATCH] {msg.symbol} | {msg.match_price} x {msg.match_qty}")
elif isinstance(msg, PortfolioMessage):
print(f" [PORTFOLIO] {msg.symbol} | Net: {msg.net}")
async def main():
async with AsyncAuth(config) as auth:
await ensure_auth_async(auth, otp="<your_otp>")
async with AsyncStream(auth) as stream:
await stream.streaming.connect()
stream.streaming.on_trading = on_trading_event
await stream.streaming.subscribe_order_status(ACCOUNT_NO)
try:
await stream.streaming.wait(timeout_ms=300_000)
except KeyboardInterrupt:
pass
asyncio.run(main())So sánh Polling vs WebSocket
| Polling (Sample 8) | WebSocket (Sample 11) | |
|---|---|---|
| Latency | Theo chu kỳ poll (3-5s) | Near real-time (<100ms) |
| Traffic | Nhiều request lặp lại | 1 kết nối duy trì |
| Complexity | Đơn giản | Cần xử lý reconnect |
| Use case | Kiểm tra nhanh | Production monitoring |