Python

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/False

Data / 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_positions và get_derivative_positions trả về None khi 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:

PropertyKiểuMô tả
on_dataCallable[[message], Any]Nhận mọi market data (tự parse thành typed message)
on_tradingCallable[[OrderStatusMessage | FCOOrderUpdateMessage | PortfolioMessage], Any]Nhận mọi trading event
on_heartbeatCallable[[HeartbeatMessage], Any]Nhận heartbeat
on_market_flagCallable[[MarketFlagMessage], Any]Cờ phiên giao dịch
on_index_summaryCallable[[IndexSummaryMessage], Any]Số liệu tổng hợp của chỉ số
on_order, on_order_match, on_portfolioCallback theo từng loại trading messageCần OTP
on_connectionCallable[[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àmTrả vềDùng với
trading_ws(auth, **options)TradingWSServiceAuth
async_trading_ws(auth, **options)AsyncTradingWSServiceAsyncAuth

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 socket

Trading 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ùng async with và await; callback của nhóm async có thể là coroutine.
  • Nhóm sync (Auth, Data, Trading, Stream) dùng with — 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ồm SmartOTPPendingError, SmartOTPRejectedError, ReauthenticationRequired), WebSocketError (stream; TradingWSError cho Trading WebSocket), APIError (HTTP lỗi nghiệp vụ; DuplicateRequestError cho HTTP 409). Xem Exceptions.

Trên trang này