Go

Client Classes

Auth, Data, Trading, Stream và Trading WebSocket — khởi tạo và sử dụng

SDK cung cấp 4 client chuyên biệt (Auth, Data, Trading, Stream), cộng thêm một client Trading WebSocket. Go sử dụng goroutines cho concurrency — không cần biến thể async riêng.

Module path là github.com/SSI-Securities-Inc/ssi-sdk-go/v3 và package gốc có tên ssisdk (chứa các kiểu lỗi, xem Xử lý lỗi). Các client được tạo qua package ssi. Yêu cầu Go 1.22 trở lên.

Mọi khoảng thời gian trong SDK đều là số nguyên tính bằng mili giây với hậu tố Ms.

Auth

Client gốc — quản lý REST client, xác thực, và token. Tất cả client khác nhận *Auth làm tham số.

import (
    "log"
    "os"

    "github.com/SSI-Securities-Inc/ssi-sdk-go/v3/ssi"
)

config := ssi.NewConfig("YOUR_CLIENT_ID")
config.APIKey     = os.Getenv("SSI_API_KEY")
config.APISecret  = os.Getenv("SSI_API_SECRET")
config.PrivateKey = os.Getenv("SSI_PRIVATE_KEY")

auth := ssi.NewAuth(config)
defer auth.Close()

token, err := auth.Authenticate("YOUR_OTP")
if err != nil {
    log.Fatal(err)
}
// Không log token.AccessToken.
_ = token

Các method xác thực

// Yêu cầu gửi OTP (map thô)
result, err := auth.RequestOTP()

// Yêu cầu gửi OTP (có kiểu: *auth.OTPResponse với Message và TransactionID)
otpRes, err := auth.RequestOTPTyped()

// Xác thực với OTP
token, err := auth.Authenticate("YOUR_OTP")

// Xác thực không cần OTP (chỉ dữ liệu thị trường)
token, err := auth.Authenticate("")

// Làm mới token
token, err := auth.Refresh()

// Xác thực hoặc refresh khi cần; trả về chuỗi access token
accessToken, err := auth.EnsureAuthenticated("YOUR_OTP")

EnsureAuthenticated(otp, transactionID...) trả về access token dạng string (không phải *Token). EnsureAuthenticatedWith(ctx, auth.EnsureOptions{...}) nhận context và cho phép đặt OTP, TransactionID, PollIntervalMs và PollMaxRetries (đều là *int).

Approve OTP (Smart OTP push-approval)

Tài khoản đã kích hoạt Smart OTP có thể duyệt trực tiếp trên app thay vì nhập mã. Gọi RequestOTPTyped() để gửi yêu cầu, lấy TransactionID, rồi truyền vào EnsureAuthenticated() — SDK tự poll cho đến khi user bấm duyệt.

// Bước 1: gửi yêu cầu duyệt, nhận transaction id
otpRes, err := auth.RequestOTPTyped()
if err != nil {
    log.Fatal(err)
}

// Bước 2: SDK tự poll cho đến khi user bấm duyệt trên app Smart OTP
// (otp truyền "" vì đang dùng transaction id)
accessToken, err := auth.EnsureAuthenticated("", otpRes.TransactionID)

Trong lúc chờ, server trả HTTP 202 + code 401114 ("Push-approval is pending") — SDK tự xử lý. Chu kỳ poll và thời gian chờ tối đa lấy từ Config.OTPPollIntervalMs (5000) và Config.OTPPollMaxWaitMs (60000); EnsureAuthenticatedWith có thể ghi đè theo từng lần gọi. Nếu hết số lần poll mà vẫn chưa duyệt, EnsureAuthenticated trả về lỗi xác thực.

Trạng thái token

auth.TokenManager.IsTokenExpired()  // true/false (token còn dưới 30 giây là coi như hết hạn)
auth.AccessToken()                  // chuỗi access token
auth.Token()                        // *auth.Token, hoặc nil
auth.HasRefreshToken()              // đang có refresh token
auth.CanRefresh()                   // refresh token còn hiệu lực

Data

Client dữ liệu thị trường. Không cần OTP — chỉ cần auth.Authenticate("").

import "github.com/SSI-Securities-Inc/ssi-sdk-go/v3/ssi"

auth := ssi.NewAuth(config)
defer auth.Close()

if _, err := auth.Authenticate(""); err != nil {
    log.Fatal(err)
}

data := ssi.NewData(auth)

ohlc, err := data.MarketData.GetOHLC1Minute("SSI")
indexes, err := data.MarketData.GetIndexes()
info, err := data.MarketData.GetSecuritiesInfo("SSI")
master, err := data.MarketData.GetMasterData() // giá trần, sàn và tham chiếu
actions, err := data.MarketData.GetCorporateActions() // sự kiện quyền của hôm nay

Service: data.MarketData (*market.Service) — OHLC, chỉ số, tổng hợp chỉ số/sàn, chứng khoán, tổng hợp chứng khoán, master data, sự kiện quyền. Xem API Services.

Trading

Client giao dịch, tài khoản, và danh mục. Cần OTP.

import (
    "github.com/SSI-Securities-Inc/ssi-sdk-go/v3/ssi"
    "github.com/SSI-Securities-Inc/ssi-sdk-go/v3/trading"
)

auth := ssi.NewAuth(config)
defer auth.Close()

if _, err := auth.Authenticate("YOUR_OTP"); err != nil {
    log.Fatal(err)
}

t := ssi.NewTrading(auth)

// Tài khoản
accounts, err := t.Account.GetAccountInfo()

// Danh mục (getter trả về nil khi không có dữ liệu: hãy kiểm tra trước khi dùng)
balance, err := t.Portfolio.GetEquityBalance("YOUR_ACCOUNT_NO")
positions, err := t.Portfolio.GetEquityPositions("YOUR_ACCOUNT_NO")
orders, err := t.Portfolio.GetTodayOrders("YOUR_ACCOUNT_NO")

// Giao dịch
result, err := t.Trading.PlaceLimitOrder(
    "YOUR_ACCOUNT_NO", "SSI", trading.OrderSideBuy, 100, 66000,
)

Services:

  • t.Account (*account.Service) — thông tin tài khoản.
  • t.Portfolio (*portfolio.Service) — số dư, vị thế, sổ lệnh, PPMMR.
  • t.Trading (*trading.Service) — đặt/sửa/huỷ lệnh, batch orders, sức mua/bán, lệnh điều kiện (FCO).

Các nhóm method của client Trading (tham số đầy đủ trong API Services):

NhómMethod
Đặt lệnhPlaceOrder, PlaceLimitOrder, PlaceMarketOrder, PlaceATOOrder, PlaceATCOrder
Sửa / huỷModifyOrderPrice, ModifyOrderQuantity, ModifyOrderPriceByOrderID, ModifyOrderQuantityByOrderID, CancelOrder, CancelOrderByOrderID
BatchPlaceBatchOrders, CancelBatchOrders — xem Batch Orders
Sức mua/bánGetMaxBuySell, GetMaxBuySellAtMarketPrice
Lệnh điều kiện (FCO)PlaceFcoGtd, PlaceFcoStop, PlaceFcoStopLimit, PlaceFcoTrailingStop, PlaceFcoTrailingStopLimit, PlaceFcoOco, PlaceFcoBullBear, CancelFco, GetFcoByAccountNo, GetFcoBySymbol, GetFcoByStatus, GetFcoByStatuses, GetFcoByDate, GetFcoById, GetFcoOrderBook, GetFcoStatusHistory

Các method đặt lệnh và FCO nhận thêm option ở cuối (tuỳ chọn): trading.WithClientRequestID(id) cho lệnh và trading.WithOTPCode(code) cho đặt/huỷ FCO.

Stream

Client streaming realtime qua WebSocket. Cần OTP.

import (
    "fmt"
    "log"

    "github.com/SSI-Securities-Inc/ssi-sdk-go/v3/ssi"
    "github.com/SSI-Securities-Inc/ssi-sdk-go/v3/stream"
)

auth := ssi.NewAuth(config)
defer auth.Close()

if _, err := auth.Authenticate("YOUR_OTP"); err != nil {
    log.Fatal(err)
}

s := ssi.NewStream(auth)
defer s.Disconnect()

// Đăng ký callback
s.Streaming.SetOnData(func(msg interface{}) {
    switch m := msg.(type) {
    case *stream.TradeMessage:
        fmt.Printf("[TRADE] %s | %.0f | %d\n", m.Symbol, m.Price, m.Quantity)
    case *stream.QuoteMessage:
        fmt.Printf("[QUOTE] %s\n", m.Symbol)
    case *stream.IndexTickMessage:
        if m.Value != nil {
            fmt.Printf("[INDEX] %s | %.2f\n", m.Symbol, *m.Value)
        }
    }
})

s.Streaming.SetOnTrading(func(msg interface{}) {
    switch m := msg.(type) {
    case *stream.OrderStatusMessage:
        fmt.Printf("[ORDER] %s %s | %s\n", m.Symbol, m.Side, m.Status)
    case *stream.OrderMatchMessage:
        fmt.Printf("[MATCH] %s | %d @ %.0f\n", m.Symbol, m.MatchQty, m.MatchPrice)
    }
})

// Kết nối và subscribe
if err := s.Connect(); err != nil {
    log.Fatal(err)
}

s.Streaming.SubscribeSymbol([]string{"SSI", "HPG"}, nil)
s.Streaming.SubscribeIndexTrade([]string{"VNINDEX"}, stream.IntervalTick, nil)
s.Streaming.SubscribeOrderStatus("", nil) // "" = tất cả tài khoản của token

s.Wait(nil) // nil = chờ đến khi ngắt kết nối

Service: s.Streaming (*stream.Service) — subscribe/unsubscribe dữ liệu realtime. Facade *ssi.Stream có Connect(), Disconnect(), IsConnected() và Wait(timeoutMs *int).

Stream tự reconnect và subscribe lại khi Config.AutoReconnect là true (mặc định). Theo dõi trạng thái bằng SetOnConnection, xem Connection events.

Callbacks:

MethodCallback SignatureMô tả
SetOnDatafunc(interface{})Mọi message kênh DATA, đã parse thành kiểu cụ thể (dùng type switch)
SetOnTradingfunc(interface{})Mọi sự kiện kênh trading (trạng thái lệnh, khớp lệnh, danh mục, FCO)
SetOnHeartbeatfunc(*stream.HeartbeatMessage)Nhận heartbeat
SetOnMarketFlagfunc(*stream.MarketFlagMessage)Chỉ cờ phiên thị trường
SetOnIndexSummaryfunc(*stream.IndexSummaryMessage)Chỉ tổng hợp chỉ số
SetOnOrderfunc(*stream.OrderStatusMessage)Chỉ sự kiện trạng thái lệnh
SetOnOrderMatchfunc(*stream.OrderMatchMessage)Chỉ sự kiện khớp lệnh
SetOnPortfoliofunc(*stream.PortfolioMessage)Chỉ sự kiện danh mục (tài khoản phái sinh)
SetOnConnectionfunc(transport.ConnectionEvent)Thay đổi trạng thái kết nối

Callback theo kiểu chỉ nhận đúng kiểu của nó và không tự subscribe gì cả; bạn vẫn phải gọi method Subscribe* tương ứng. Callback bị panic sẽ được recover và không ảnh hưởng callback khác. Tick của chỉ số không có callback riêng: dùng SetOnData và type switch. Các method subscribe, quy tắc topic và interval nằm ở API Services, mục 6.

Client Trading WebSocket

Package tradingws không nằm trong facade Trading / Stream; hãy tạo client từ một *ssi.Auth.

tradingws.TradingWS kết nối tới Trading WebSocket (/ws/v3/trading) và trả về *tradingws.Service để gửi lệnh, chạy truy vấn và nhận sự kiện lệnh trên một socket. Khác với bốn client ở trên, nó được tạo từ *ssi.Auth và một context.Context, và kết nối ngay lập tức.

Đến 3.3.1 package này tên là experimental. Khi nâng cấp, đổi import thành .../v3/tradingws và experimental. thành tradingws., xem ghi chú nâng cấp.

import (
    "context"
    "log"

    "github.com/SSI-Securities-Inc/ssi-sdk-go/v3/ssi"
    "github.com/SSI-Securities-Inc/ssi-sdk-go/v3/tradingws"
)

auth := ssi.NewAuth(config) // config.PrivateKey bắt buộc để ký lệnh
defer auth.Close()
if _, err := auth.Authenticate("YOUR_OTP"); err != nil {
    log.Fatal(err)
}

ctx := context.Background()
ws, err := tradingws.TradingWS(ctx, auth)
if err != nil {
    log.Fatal(err)
}
defer ws.Close() // bắt buộc: đóng socket và đánh thức mọi request đang chờ

Cần biết:

  • Giá trị mặc định lấy từ Config (TradingWSDomain, TimeoutMs, UserAgent, PrivateKey); chỉ chấp nhận URL wss://.
  • Không tự reconnect: gọi ws.Client().Connect(ctx) rồi subscribe lại.
  • Method, option, hành vi và ví dụ: Trading WebSocket. Cài đặt: Cấu hình.

Ví dụ đầy đủ

package main

import (
    "fmt"
    "log"
    "os"

    "github.com/SSI-Securities-Inc/ssi-sdk-go/v3/ssi"
    "github.com/SSI-Securities-Inc/ssi-sdk-go/v3/stream"
)

func main() {
    config := ssi.NewConfig(os.Getenv("SSI_CLIENT_ID"))
    config.APIKey     = os.Getenv("SSI_API_KEY")
    config.APISecret  = os.Getenv("SSI_API_SECRET")
    config.PrivateKey = os.Getenv("SSI_PRIVATE_KEY")

    auth := ssi.NewAuth(config)
    defer auth.Close()

    if _, err := auth.Authenticate(os.Getenv("SSI_OTP")); err != nil {
        log.Fatal(err)
    }

    // Dữ liệu thị trường
    data := ssi.NewData(auth)
    ohlc, err := data.MarketData.GetOHLC1Minute("SSI")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(ohlc)

    // Giao dịch
    t := ssi.NewTrading(auth)
    accounts, err := t.Account.GetAccountInfo()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(accounts)

    // Streaming
    s := ssi.NewStream(auth)
    defer s.Disconnect()

    s.Streaming.SetOnData(func(msg interface{}) {
        switch m := msg.(type) {
        case *stream.TradeMessage:
            fmt.Printf("[TRADE] %s | %.0f | %d\n", m.Symbol, m.Price, m.Quantity)
        }
    })

    s.Streaming.SetOnTrading(func(msg interface{}) {
        fmt.Printf("[TRADING] %v\n", msg)
    })

    if err := s.Connect(); err != nil {
        log.Fatal(err)
    }

    s.Streaming.SubscribeSymbol([]string{"SSI", "HPG"}, nil)
    s.Streaming.SubscribeOrderStatus("", nil)

    timeoutMs := 30000 // 30 giây
    s.Wait(&timeoutMs)
}

Ghi chú

  • Go SDK không có biến thể async riêng — dùng goroutines cho concurrency.
  • Tất cả client dùng chung HTTP connection qua Auth.
  • Luôn dùng defer auth.Close() và defer s.Disconnect() để dọn dẹp (và defer ws.Close() cho client Trading WebSocket).
  • ssi.NewClient đã deprecated: dùng ssi.NewAuth cùng NewData / NewTrading / NewStream.
  • Đọc secret và OTP từ biến môi trường hoặc secret store; không hard-code và không log token.
  • Phân loại lỗi bằng errors.As với các kiểu trong package ssisdk: *ssisdk.AuthenticationError (xác thực), *ssisdk.WebSocketError (stream), *ssisdk.TradingWSError (Trading WebSocket), *ssisdk.APIError (lỗi nghiệp vụ HTTP). Xem Xử lý lỗi.

Trên trang này