Tổng quan luồng kết nối
Thông tin kết nối chung
- Base URL:
wss://ws-openapi.dnse.com.vn - Định dạng dữ liệu:
msgpack: Tốc độ xử lý nhanh, tiết kiệm băng thôngjson: Phổ biến và dễ đọc trong quá trình phát triển
- Cơ chế kết nối:
- Tất cả mã chứng khoán phải ở định dạng chữ in hoa. VD: ACB, HPG, 41I1G2000.
- Một kết nối WebSocket có hiệu lực tối đa 8 giờ, WebSocket Server sẽ chủ động ngắt kết nối sau thời gian này.
- Cơ chế để các clients duy trì kết nối ổn định tới WebSocket server DNSE:
- WebSocket Server sẽ định kỳ gửi 1 PING message sau mỗi 3 phút.
- Mỗi PING message được gửi từ WebSocket đều yêu cầu nhận PONG message phản hồi từ các client trong thời gian tối đa là 1 phút kể từ lúc Server gửi PING. Nếu quá thời hạn 1 phút này, Server sẽ chủ động ngắt kết nối với Client không đáp ứng.
- Client được phép gửi PONG message ngay cả khi không nhận được PING từ Server, để chủ động duy trì kết nối. Cách này giúp client giữ kết nối trong các trường hợp PING message bị miss do network issue hoặc các gián đoạn tạm thời khác.
Bước 1 — Kết nối
Endpoint:encoding | json hoặc msgpack | Định dạng toàn bộ message trong session. Khi dùng
msgpack, server gửi binary WebSocket frameKhi dùng
json, server gửi text frame UTF-8. | | Sau khi kết nối thành công, nhận welcome message:
session_id dùng để định danh kết nối, lưu lại để tiện debug/logging. Người dùng nhận được message này là tín hiệu sẵn sàng để thực hiện bước auth.
Lưu ý: Client phải gửi auth message và auth thành công trong vòng 30 giây kể từ khi kết nối, nếu không server sẽ đóng kết nối ({ "action": "error", "code": "AUTH_TIMEOUT" }).
Bước 2 — Xác thực (HMAC-SHA256)
Client phải gửi auth message. Server không chấp nhận subscribe trước khi auth thành công.Tạo chữ ký
Ràng buộc quan trọng:
timestampphải nằm trong khoảng ±5 phút so với giờ server, nếu không auth bị từ chối (timestamp outside valid window). Đảm bảo đồng hồ client được đồng bộ NTP.noncekhông được tái sử dụng trong vòng 10 phút (server lưu để chống replay). Mỗi lần auth — kể cả khi reconnect — phải tạononcemới.
Auth Request
Auth Response
Thành công (ngoàiaction còn kèm một số trường thông tin, client có thể bỏ qua nếu không cần):
error (không có action auth_error) rồi đóng kết nối:
Ví dụ signature (Python)
Ví dụ tính signature (JavaScript)
Bước 3 — Subscribe stream
Sau khi auth thành công, gửi subscribe message để bắt đầu nhận dữ liệu.Request
Channel theo từng loại dữ liệu:
Giá trị hợp lệ của
boardId, resolution, marketIndex xem tại Enum dữ liệu thị trường.
Response
Subscribe nhiều channel cùng lúc
Bước 4 — Đọc dữ liệu inbound
Sau khi subscribe, client đọc message liên tục từ WebSocket. Có 2 loại message: Control messages — message điều khiển vòng đời kết nối/subscription:Message lỗi có dạngData messages — payload thị trường thực tế, nhận biết qua trường{ "action": "error", "code": "...", "message": "..." }— dùng trườngmessage(không phảimsg).
"T".
⚠️ Không phân loại bằng “có trườngactionhay không”. Phần lớn data message không cóaction, nhưng một số loại (ví dụestimated_market_index) lại có cảactionlẫnT. Cách an toàn là: chỉ coi là control message khiactionthuộc tập các action điều khiển đã biết ở trên; còn lại (hoặc khi có trườngT) là data message.
Bước 5 — Unsubscribe
Bước 6 — Duy trì kết nối (PING/PONG)
Server và client dùng application-level PING/PONG (trườngaction trong JSON/msgpack), khác với WebSocket protocol-level ping/pong frame.
Server gửi định kỳ (kèm timestamp tính bằng milliseconds):
1008):
Để server đo được round-trip latency, client nên echo lạiKhuyến nghị: Client nên chủ động gửitimestampnhận được trongpong:{ "action": "pong", "timestamp": 1750000000123 }. Nếu bỏ trường này, keepalive vẫn hoạt động bình thường, chỉ là không có số liệu latency.
ping mỗi 25 giây để phòng trường hợp PING từ server bị miss do NAT timeout hoặc mobile network. Khi client gửi ping, server trả về pong (kèm timestamp tính bằng giây).
Lưu ý: SDK Python mặc định gửi ping mỗi 25 giây (heartbeat_interval=25.0). Khi tự implement, hãy giữ interval tương đương.
Reconnection
Khi kết nối bị ngắt, client cần thực hiện lại toàn bộ luồng từ đầu:- Kết nối lại → nhận welcome message mới (session_id mới)
- Gửi lại auth message — phải tạo
timestampvànoncemới, không tái dùng giá trị cũ - Gửi lại tất cả subscribe message
Các close code không do server gửi mà sinh ra ở phía client/hạ tầng mạng — vẫn nên reconnect:
Hết 8 giờ: Trước khi đóng, server gửiNên dùng exponential backoff để tránh reconnect liên tục:{ "action": "connection_expired", "code": "MAX_DURATION_EXCEEDED", "message": "Connection expired after 8 hours. Please reconnect." }rồi đóng với code1008. Client nên reconnect ngay (đây là hành vi bình thường, không phải lỗi).
1s → 2s → 4s → 8s → ... → 60s (tối đa).
Ví dụ hoàn chỉnh (Python)
Tham khảo thêm
- Danh sách đầy đủ channel name và payload → Market Data WebSocket
- SDK chính thức đã có sẵn (đã xử lý auth, reconnect, keepalive) → DNSE sample SDKs