Skip to main content
Trang này mô tả chi tiết WebSocket protocol của DNSE để bạn tự implement client bằng bất kỳ ngôn ngữ nào. DNSE cung cấp sẵn bộ SDK đã phân tách theo từng loại dữ liệu để khách hàng có thể sử dụng ngay. Chi tiết xem tại DNSE sample SDKs

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ông
    • json: 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:
| Query param | Giá trị | Mô tả | | ----------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | encoding | json hoặc msgpack | Định dạng toàn bộ message trong session.
Khi dùng msgpack, server gửi binary WebSocket frame
Khi 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:
  • timestamp phả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.
  • nonce khô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ạo nonce mới.

Auth Request

Auth Response

Thành công (ngoài action còn kèm một số trường thông tin, client có thể bỏ qua nếu không cần):
Thất bại — server trả về control message 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ạng { "action": "error", "code": "...", "message": "..." } — dùng trường message (không phải msg).
Data messages — payload thị trường thực tế, nhận biết qua trường "T".
⚠️ Không phân loại bằng “có trường action hay 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ả action lẫn T. Cách an toàn là: chỉ coi là control message khi action thuộc tập các action điều khiển đã biết ở trên; còn lại (hoặc khi có trường T) là data message.
Cấu trúc payload của từng loại xem tại Market Data WebSocket

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ường action 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):
Client phải phản hồi trong vòng 60 giây, nếu không server sẽ đóng kết nối (close code 1008):
Để server đo được round-trip latency, client nên echo lại timestamp nhận được trong pong: { "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.
Khuyến nghị: Client nên chủ động gửi 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:
  1. Kết nối lại → nhận welcome message mới (session_id mới)
  2. Gửi lại auth message — phải tạo timestamp và nonce mới, không tái dùng giá trị cũ
  3. Gửi lại tất cả subscribe message
WebSocket close code: Các close code server chủ động gửi: 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ửi { "action": "connection_expired", "code": "MAX_DURATION_EXCEEDED", "message": "Connection expired after 8 hours. Please reconnect." } rồi đóng với code 1008. Client nên reconnect ngay (đây là hành vi bình thường, không phải lỗi).
Nên dùng exponential backoff để tránh reconnect liên tục: 1s → 2s → 4s → 8s → ... → 60s (tối đa).

Ví dụ hoàn chỉnh (Python)

Tham khảo thêm