Client classes
Auth, Data, Trading, Stream, Trading WebSocket — khởi tạo và sử dụng
SDK 3.3.2 cung cấp 4 client chuyên biệt, mỗi client có phiên bản sync và async. Ngoài ra có Trading WebSocket, xem Trading WebSocket.
Auth / AsyncAuth
Client gốc — quản lý REST client, xác thực và token. Tất cả client khác đều nhận Auth làm tham số.
from ssi_sdk import Auth, Config
config = Config(
client_id="<CLIENT_ID>",
api_key="<API_KEY>",
api_secret="<API_SECRET>",
private_key="<PRIVATE_KEY_CONTENT>",
)
with Auth(config) as auth:
# Xác thực với OTP
token = auth.authenticate(otp="<OTP>")
print(f"Expires at: {token.expires_at}")Không in hoặc log token.access_token. Mọi thời lượng trong Config tính bằng mili giây (hậu tố _ms), xem Configuration.
Các method xác thực
# Yêu cầu gửi OTP (trả về dict thô)
result = auth.request_otp()
# Như trên nhưng trả về OTPResponse(message, transaction_id)
otp = auth.request_otp_typed()
# Xác thực với OTP
token = auth.authenticate(otp="<OTP>")
# Làm mới token
token = auth.refresh()
# Tự động refresh nếu token hết hạn, hoặc xác thực nếu cần
access_token = auth.ensure_authenticated(otp="<OTP>")otp và transaction_id loại trừ nhau: chỉ truyền một trong hai. Token được coi là hết hạn sớm 30 giây. Mỗi apiKey chỉ có 1 session hoạt động.
Smart OTP (duyệt qua app)
Tài khoản đã kích hoạt Smart OTP duyệt trực tiếp trên app thay vì nhập mã. Gọi request_otp_typed() để gửi yêu cầu, lấy transaction_id, rồi truyền vào ensure_authenticated(). SDK tự poll cho đến khi user bấm duyệt.
from ssi_sdk.exceptions import (
ReauthenticationRequired,
SmartOTPPendingError,
SmartOTPRejectedError,
)
# Bước 1: gửi yêu cầu duyệt, nhận transaction_id
otp = auth.request_otp_typed()
# Bước 2: SDK tự poll cho đến khi user bấm duyệt trên app Smart OTP
try:
access_token = auth.ensure_authenticated(
transaction_id=otp.transaction_id,
poll_interval_ms=5000, # mili giây giữa mỗi lần poll (mặc định Config.otp_poll_interval_ms = 5000)
poll_max_retries=12, # số lần poll tối đa (mặc định suy ra từ Config.otp_poll_max_wait_ms = 60000)
)
except SmartOTPPendingError:
# Chưa duyệt kịp: gọi lại với cùng transaction_id
...
except SmartOTPRejectedError:
# Bị từ chối / hết hạn: yêu cầu OTP mới
...
except ReauthenticationRequired:
# Không refresh được: xác thực lại bằng OTP mới
...Trong lúc chờ, server trả HTTP 202 + code 401114 ("Push-approval is pending"), SDK tự xử lý. Nếu hết số lần poll mà vẫn chưa duyệt, ensure_authenticated raise SmartOTPPendingError. poll_interval_ms và poll_max_retries là tùy chọn; bỏ qua thì SDK dùng Config.otp_poll_interval_ms và Config.otp_poll_max_wait_ms. Chi tiết lỗi: Exceptions. Model OTPResponse: Models.
Kiểm tra trạng thái token
print(auth.token) # Token object hoặc None
# auth.access_token # Access token string hoặc None (không in/log)
print(auth.is_token_expired) # True/False
print(auth.has_refresh_token) # True/FalseData / AsyncData
Client dữ liệu thị trường. Không cần OTP — chỉ cần auth.authenticate().
from ssi_sdk import Auth, Data, Config
with Auth(config) as auth:
auth.authenticate() # Không cần OTP cho market data
with Data(auth) as data:
# Truy cập qua service
ohlc = data.market_data.get_ohlc_1minute("SSI")
indices = data.market_data.get_indexes()
info = data.market_data.get_securities_info("SSI")Service: data.market_data (MarketDataService) — OHLC, chỉ số, chứng khoán, securities summary, master data, corporate actions. Chi tiết: Services, mục 3.
Trading / AsyncTrading
Client giao dịch, tài khoản và danh mục. Cần OTP.
from ssi_sdk import Auth, Trading, Config
from ssi_sdk.enums import OrderSide
with Auth(config) as auth:
auth.authenticate(otp="<OTP>")
with Trading(auth) as trading:
# Tài khoản
accounts = trading.account.get_account_info()
# Danh mục (có thể trả về None khi tài khoản chưa có dữ liệu)
balance = trading.portfolio.get_equity_balance("<EQUITY_ACCOUNT_NO>")
positions = trading.portfolio.get_equity_positions("<EQUITY_ACCOUNT_NO>")
orders = trading.portfolio.get_today_orders("<EQUITY_ACCOUNT_NO>")
# Giao dịch
result = trading.trading.place_limit_order(
account_no="<EQUITY_ACCOUNT_NO>",
symbol="SSI",
side=OrderSide.BUY,
quantity=100,
price=66000,
)Services:
trading.account(AccountService) — thông tin tài khoản.trading.portfolio(PortfolioService) — số dư, vị thế, sổ lệnh, PPMMR.get_equity_positionsvàget_derivative_positionstrả vềNonekhi không có vị thế (hoặc server trả 204).trading.trading(TradingService) — đặt/sửa/huỷ lệnh, batch order, sức mua/bán, lệnh điều kiện (FCO).
Batch Order (đặt/hủy nhiều lệnh trong một request) là method của trading.trading, xem Batch Order. Giới hạn mỗi batch lấy từ Config.max_batch_orders (mặc định 20).
Stream / AsyncStream
Client streaming realtime qua WebSocket. Kênh dữ liệu thị trường không cần OTP riêng; kênh trading (on_order, on_order_match, on_portfolio) cần OTP.
from ssi_sdk import Auth, Stream, Config
with Auth(config) as auth:
auth.authenticate(otp="<OTP>")
with Stream(auth) as stream:
# Đăng ký callback TRƯỚC khi connect
stream.streaming.on_data = lambda msg: print(f"Data: {msg}")
stream.streaming.on_trading = lambda msg: print(f"Trading: {msg}")
stream.streaming.on_connection = lambda ev: print(f"Conn: {ev.state}")
stream.streaming.connect()
# Subscribe
stream.streaming.subscribe_symbol(["SSI", "HPG"])
stream.streaming.subscribe_order_status()
# Chờ nhận dữ liệu (mili giây; bỏ trống = chờ đến khi đóng)
stream.streaming.wait(timeout_ms=30_000)Service: stream.streaming (StreamingService) — subscribe/unsubscribe realtime data. Kết nối tự reconnect khi Config.auto_reconnect bật (mặc định True).
Callbacks:
| Property | Kiểu | Mô tả |
|---|---|---|
on_data | Callable[[message], Any] | Nhận mọi market data (tự parse thành typed message) |
on_trading | Callable[[OrderStatusMessage | FCOOrderUpdateMessage | PortfolioMessage], Any] | Nhận mọi trading event |
on_heartbeat | Callable[[HeartbeatMessage], Any] | Nhận heartbeat |
on_market_flag | Callable[[MarketFlagMessage], Any] | Cờ phiên giao dịch |
on_index_summary | Callable[[IndexSummaryMessage], Any] | Số liệu tổng hợp của chỉ số |
on_order, on_order_match, on_portfolio | Callback theo từng loại trading message | Cần OTP |
on_connection | Callable[[ConnectionEvent], Any] | Trạng thái kết nối: connected, reconnected, disconnected, failed |
Callback typed không thay thế on_data / on_trading, và gắn callback không tự subscribe. Dữ liệu chỉ số thời gian thực (tick chỉ số, nến 1m/5m, index summary, market flag) xem Services, mục 6.7; mô hình message xem Models: Streaming Messages; trạng thái kết nối và reconnect xem mục 6.9.
Trading WebSocket
Import từ ssi_sdk. Ở 3.3.1 trở về trước các tên này nằm trong ssi_sdk.experimental; module đó đã bị xoá ở 3.3.2 (import sẽ ném ModuleNotFoundError).
Khác với Trading / Stream, Trading WebSocket không có class client riêng trong facade: bạn tạo bằng hàm factory, truyền vào Auth / AsyncAuth đã xác thực (token lấy từ auth.token_manager).
| Hàm | Trả về | Dùng với |
|---|---|---|
trading_ws(auth, **options) | TradingWSService | Auth |
async_trading_ws(auth, **options) | AsyncTradingWSService | AsyncAuth |
URL lấy từ Config.trading_ws_domain (mặc định wss://api.ssi.com.vn/ws/v3/trading). Các option (request_timeout_ms, signature_encoding, handshake_grace_ms, respect_rate_limit, max_rate_limit_wait_ms, ping_interval_ms, user_agent) không nằm trong Config, truyền trực tiếp vào hàm factory. Lệnh thay đổi trạng thái cần Config.private_key.
from ssi_sdk import Auth, Config, TradingWSError, trading_ws
with Auth(config) as auth:
auth.ensure_authenticated(otp="<OTP>")
try:
ws = trading_ws(auth)
except TradingWSError as e:
raise SystemExit(f"Server từ chối kết nối: {e}")
try:
book = ws.query_order_book("<EQUITY_ACCOUNT_NO>", page_size=10)
print(book.status)
finally:
ws.close() # luôn đóng socketTrading WS không tự reconnect, và command hết thời gian chờ không bao giờ được SDK gửi lại. Danh sách method, scope, rate limit, idempotency và ví dụ đầy đủ: Services, mục 7.
Async example hoàn chỉnh
import asyncio
from ssi_sdk import AsyncAuth, AsyncData, AsyncTrading, AsyncStream, Config
async def main():
config = Config(
client_id="<CLIENT_ID>",
api_key="<API_KEY>",
api_secret="<API_SECRET>",
private_key="<PRIVATE_KEY_CONTENT>",
)
async with AsyncAuth(config) as auth:
await auth.authenticate(otp="<OTP>")
async with AsyncData(auth) as data:
ohlc = await data.market_data.get_ohlc_1minute("SSI")
print(ohlc)
async with AsyncTrading(auth) as trading:
accounts = await trading.account.get_account_info()
print(accounts)
async with AsyncStream(auth) as stream:
stream.streaming.on_data = lambda msg: print(msg)
await stream.streaming.connect()
await stream.streaming.subscribe_symbol_trade(["SSI"])
await stream.streaming.wait(timeout_ms=30_000)
asyncio.run(main())Ghi chú
- Nhóm
Async*dùngasync withvàawait; callback của nhóm async có thể là coroutine. - Nhóm sync (
Auth,Data,Trading,Stream) dùngwith— phù hợp luồng xử lý tuần tự. - Tất cả client chia sẻ HTTP connection qua
Auth. - Lỗi thường gặp:
AuthenticationError(xác thực, gồmSmartOTPPendingError,SmartOTPRejectedError,ReauthenticationRequired),WebSocketError(stream;TradingWSErrorcho Trading WebSocket),APIError(HTTP lỗi nghiệp vụ;DuplicateRequestErrorcho HTTP 409). Xem Exceptions.