Skip to main content
Nhận bảng giá phái sinh (VN30F, VN30F1M, VN30F2M…) theo thời gian thực qua WebSocket. Một kết nối duy nhất — server đẩy giá khớp, sổ lệnh bid/offer top 3, và thông tin phiên (high, low, open, avg) mỗi khi có thay đổi. Trang này dành cho phái sinh (endpoint /stream/derivative). So với cổ phiếu cơ sở: channel ID khác (21/23/24/27), data type là number thay vì string. Xác thực và heartbeat giống nhau.

Demo tương tác

Địa chỉ WSS
Payload kết nối / xác thực
Kênh / Chủ đề
  • s|21 (ticker match) — Giao dịch khớp: giá, khối lượng, thay đổi.
  • s|23 (bid info) — Sổ lệnh mua top 3: giá và khối lượng.
  • s|24 (offer info) — Sổ lệnh bán top 3: giá và khối lượng.
  • s|27 (day summary) — Thông tin phiên: cao, thấp, trung bình, mở cửa.
Demo trên chỉ mang tính minh họa. Protocol thực tế trên wire là TEXT frame phân tách bằng pipe (|), KHÔNG phải JSON. Xem các phần bên dưới để biết chi tiết format thật.

Kết nối

WSS /ws/thesis/v1/stream/derivative Địa chỉ WebSocket đầy đủ mà bạn cần connect tới:
BẮT BUỘC: Phải disable auto-ping (RFC 6455 ping frame) của WebSocket library. Server TCBS chỉ chấp nhận heartbeat dạng text frame d|p|||. Nếu library tự gửi binary ping → server trả 1002 Protocol Error và ngắt kết nối sau ~15-20 giây.
Dưới đây là cách disable auto-ping cho từng ngôn ngữ/thư viện phổ biến:

Xác thực (Authentication)

Sau khi kết nối WebSocket thành công, bạn phải gửi auth message trước khi subscribe bất kỳ kênh nào. Lưu ý: JWT token từ API đăng nhập cần được base64 encode trước khi đưa vào message. Client gửi auth message theo format sau:
Server trả về khi auth thành công — bạn sẽ nhận frame xác nhận:
Server cũng gửi thêm một frame cấu hình timeout (15 = số giây heartbeat timeout):
Server trả về khi auth thất bại:
PHẢI đợi nhận response auth d|0|{...success:true...} trước khi gửi subscribe. Gửi subscribe ngay mà không đợi → có thể bị server reject.
Python

Đăng ký kênh (Subscribe)

Sau khi nhận auth thành công, bạn gửi subscribe message để bắt đầu nhận dữ liệu realtime. Format như sau:
Ví dụ: subscribe đầy đủ tất cả kênh cho VN30F front-month — client gửi:
Dưới đây là bảng channel codes và ý nghĩa tương ứng: Bạn có thể gửi bp và fe trong subscribe request (cùng format 6 channel code như cổ phiếu cơ sở), nhưng server phái sinh chỉ phát dữ liệu trên channel 21/23/24/27 (tm/bi/op/mp). Các code bp và fe sẽ được chấp nhận nhưng không tạo ra frame nào.

Heartbeat — Giữ kết nối

Client PHẢI gửi heartbeat text frame mỗi 2 giây để giữ kết nối sống. Nếu server không nhận heartbeat trong 15 giây → tự động ngắt. Frame heartbeat mà client gửi:
Heartbeat phải là TEXT frame, KHÔNG phải binary/ping frame. Đây là nguyên nhân #1 gây disconnect liên tục mà developer khó debug.

Cấu trúc Message

Server gửi về 2 loại message. Bạn phân biệt chúng qua prefix đầu tiên: Data message mà server push về có format như sau:
Ví dụ thực tế — server gửi một giao dịch khớp trên channel 21:
Control messages — bạn có thể bỏ qua khi parse data. Server gửi các dạng sau:

Channel ID & Data Schema

Channel 21 — Ticker Match (Giao dịch khớp)

Mỗi lệnh khớp, server push 1 message dạng JSON trên channel 21:
Channel 21 — Ticker Match

Channel 23 — Bid Info (Sổ lệnh mua — Top 3)

Server push sổ lệnh mua top 3 trên channel 23:
Channel 23 — Bid Info

Channel 24 — Offer Info (Sổ lệnh bán — Top 3)

Server push sổ lệnh bán top 3 trên channel 24:
Channel 24 — Offer Info

Channel 27 — Day Summary (Thông tin phiên)

Server push thông tin tổng hợp phiên trên channel 27:
Channel 27 — Day Summary

So sánh Channel ID: Phái sinh vs Cổ phiếu

Channel ID hoàn toàn khác giữa 2 endpoint. KHÔNG dùng chung parser cho cổ phiếu và phái sinh.

Mã hợp đồng phái sinh (Symbol)

Phái sinh sử dụng mã nội bộ TCBS, KHÔNG phải mã HNX/HSX. Ví dụ: 41I1G8000 = VN30F front-month (tháng gần nhất). Mã này sẽ thay đổi khi rollover sang kỳ hạn mới. Để lấy danh sách mã active hiện tại, bạn gọi API sau: GET /api/v1/derivatives/contracts
Về rollover: mã front-month thay đổi vào ngày đáo hạn — thường là thứ Năm tuần thứ 3 của tháng. Sau rollover, mã cũ ngừng push data. Bạn cần re-subscribe với mã mới.

Sequence Diagram — Luồng hoàn chỉnh

Luồng kết nối WebSocket phái sinh

Xử lý lỗi & Reconnect

Nguyên nhân disconnect

Chiến lược reconnect (khuyến nghị)

Dùng exponential backoff: delay = min(2^attempt, 30) giây. Tối đa 10 lần thử, sau đó dừng hẳn và gửi alert cho bạn xử lý thủ công.

Token lifecycle

JWT token có exp claim — hiệu lực tối đa 8 giờ. Trước khi expire, bạn nên gọi refresh token API. Sau khi expire, server sẽ ngắt connection → bạn cần reconnect với token mới.

Ngoài giờ giao dịch

Phiên phái sinh chạy 8:45–11:30 và 13:00–14:45 (ngày thường). Ngoài giờ, bạn vẫn connect bình thường, heartbeat vẫn cần gửi, nhưng sẽ không có data message nào. Sáng hôm sau data tự chảy lại mà không cần reconnect.

Code hoàn chỉnh


FAQ — Câu hỏi thường gặp

99% là do library tự gửi RFC ping frame. Bạn phải disable auto-ping và tự gửi d|p||| mỗi 2s.
Phái sinh dùng channel ID khác hoàn toàn: 21, 23, 24, 27. Xem bảng mapping ở phần Channel ID phía trên.
Phái sinh trả number (1870.5), KHÔNG phải string. Khác với cổ phiếu cơ sở (trả string). Khuyến nghị: bạn nên luôn cast sang float khi parse cho an toàn.
Đây là mã nội bộ TCBS cho VN30F front-month. Bạn gọi API GET /api/v1/derivatives/contracts để lấy mapping mã hiện tại.
Được. Connection sống bình thường, nhưng không có data cho đến khi phiên mở lúc 8:45.
Token hiệu lực tối đa 8 giờ. Server ngắt connection khi token hết hạn. Bạn cần detect disconnect → refresh token → reconnect.
Hiện chưa có document chính thức. Trong thực tế, 1 connection per token là safe. Mở nhiều connection cùng token có thể bị reject.