Go

Client Classes

Auth, Data, Trading, Stream and the Trading WebSocket — initialization and usage

The SDK provides 4 specialized clients (Auth, Data, Trading, Stream), plus a Trading WebSocket client. Go uses goroutines natively for concurrency — no separate async variants needed.

The module path is github.com/SSI-Securities-Inc/ssi-sdk-go/v3 and its root package is named ssisdk (it holds the error types, see Error Handling). The clients are created through the ssi package. Go 1.22 or later is required.

Every duration in the SDK is an integer in milliseconds with an Ms suffix.

Auth

Root client — manages REST client, authentication, and tokens. All other clients receive *Auth as a parameter.

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)
}
// Do not log token.AccessToken.
_ = token

Authentication methods

// Request OTP (raw map)
result, err := auth.RequestOTP()

// Request OTP (typed: *auth.OTPResponse with Message and TransactionID)
otpRes, err := auth.RequestOTPTyped()

// Authenticate with OTP
token, err := auth.Authenticate("YOUR_OTP")

// Authenticate without OTP (market data only)
token, err := auth.Authenticate("")

// Refresh token
token, err := auth.Refresh()

// Authenticate or refresh as needed; returns the access token string
accessToken, err := auth.EnsureAuthenticated("YOUR_OTP")

EnsureAuthenticated(otp, transactionID...) returns the access token as a string (not a *Token). EnsureAuthenticatedWith(ctx, auth.EnsureOptions{...}) takes a context and lets you set OTP, TransactionID, PollIntervalMs and PollMaxRetries (both are *int).

Approve OTP (Smart OTP push-approval)

Accounts with Smart OTP enabled can approve directly in the app instead of entering a code. Call RequestOTPTyped() to send the request and get a TransactionID, then pass it to EnsureAuthenticated() — the SDK polls automatically until the user approves.

// Step 1: send the approval request, get the transaction id
otpRes, err := auth.RequestOTPTyped()
if err != nil {
    log.Fatal(err)
}

// Step 2: the SDK polls until the user approves in the Smart OTP app
// (otp is "" since we're using the transaction id instead)
accessToken, err := auth.EnsureAuthenticated("", otpRes.TransactionID)

While waiting, the server returns HTTP 202 + code 401114 ("Push-approval is pending") — the SDK handles this automatically. The poll interval and maximum wait come from Config.OTPPollIntervalMs (5000) and Config.OTPPollMaxWaitMs (60000); EnsureAuthenticatedWith can override them per call. If the attempts run out without approval, EnsureAuthenticated returns an authentication error.

Token status

auth.TokenManager.IsTokenExpired()  // true/false (treats a token within 30 s of expiry as expired)
auth.AccessToken()                  // access token string
auth.Token()                        // *auth.Token, or nil
auth.HasRefreshToken()              // a refresh token is held
auth.CanRefresh()                   // the refresh token is still valid

Data

Market data client. No OTP needed — just 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() // ceiling, floor and reference price
actions, err := data.MarketData.GetCorporateActions() // corporate actions of today

Service: data.MarketData (*market.Service) — OHLC, indexes, index/board summaries, securities, securities summary, master data, corporate actions. See API Services.

Trading

Trading, account, and portfolio client. OTP required.

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)

// Account
accounts, err := t.Account.GetAccountInfo()

// Portfolio (a getter returns nil when there is no data: check before use)
balance, err := t.Portfolio.GetEquityBalance("YOUR_ACCOUNT_NO")
positions, err := t.Portfolio.GetEquityPositions("YOUR_ACCOUNT_NO")
orders, err := t.Portfolio.GetTodayOrders("YOUR_ACCOUNT_NO")

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

Services:

  • t.Account (*account.Service) — account information.
  • t.Portfolio (*portfolio.Service) — balances, positions, order book, PPMMR.
  • t.Trading (*trading.Service) — place/modify/cancel orders, batch orders, max buy/sell, conditional orders (FCO).

Trading client method groups (full parameters in API Services):

GroupMethods
PlacePlaceOrder, PlaceLimitOrder, PlaceMarketOrder, PlaceATOOrder, PlaceATCOrder
Modify / cancelModifyOrderPrice, ModifyOrderQuantity, ModifyOrderPriceByOrderID, ModifyOrderQuantityByOrderID, CancelOrder, CancelOrderByOrderID
BatchPlaceBatchOrders, CancelBatchOrders — see Batch Orders
Max buy/sellGetMaxBuySell, GetMaxBuySellAtMarketPrice
Conditional (FCO)PlaceFcoGtd, PlaceFcoStop, PlaceFcoStopLimit, PlaceFcoTrailingStop, PlaceFcoTrailingStopLimit, PlaceFcoOco, PlaceFcoBullBear, CancelFco, GetFcoByAccountNo, GetFcoBySymbol, GetFcoByStatus, GetFcoByStatuses, GetFcoByDate, GetFcoById, GetFcoOrderBook, GetFcoStatusHistory

Order and FCO methods take optional trailing options: trading.WithClientRequestID(id) for orders and trading.WithOTPCode(code) for FCO place/cancel.

Stream

Realtime streaming client via WebSocket. OTP required.

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()

// Register callbacks
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)
    }
})

// Connect and 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) // "" = all accounts of the token

s.Wait(nil) // nil = wait until disconnected

Service: s.Streaming (*stream.Service) — subscribe/unsubscribe realtime data. The facade *ssi.Stream has Connect(), Disconnect(), IsConnected() and Wait(timeoutMs *int).

The stream reconnects and resubscribes by itself when Config.AutoReconnect is true (the default). Watch the state with SetOnConnection, see Connection events.

Callbacks:

MethodCallback SignatureDescription
SetOnDatafunc(interface{})Every DATA-channel message, parsed to a typed value (use a type switch)
SetOnTradingfunc(interface{})Every trading-channel event (order status, fills, portfolio, FCO)
SetOnHeartbeatfunc(*stream.HeartbeatMessage)Receives heartbeat
SetOnMarketFlagfunc(*stream.MarketFlagMessage)Only market session flags
SetOnIndexSummaryfunc(*stream.IndexSummaryMessage)Only index summaries
SetOnOrderfunc(*stream.OrderStatusMessage)Only order status events
SetOnOrderMatchfunc(*stream.OrderMatchMessage)Only fill events
SetOnPortfoliofunc(*stream.PortfolioMessage)Only portfolio events (derivative accounts)
SetOnConnectionfunc(transport.ConnectionEvent)Connection state changes

Typed callbacks only receive their own type and do not subscribe to anything; you still call the matching Subscribe* method. A callback that panics is recovered and does not affect the others. Index ticks have no dedicated callback: use SetOnData and a type switch. The subscribe methods, topic and interval rules are in API Services, section 6.

Trading WebSocket client

The tradingws package is not wired into the Trading / Stream facades; create the client from an *ssi.Auth.

tradingws.TradingWS connects to the Trading WebSocket (/ws/v3/trading) and returns a *tradingws.Service that sends orders, runs queries and receives order events on one socket. Unlike the four clients above, it is created from an *ssi.Auth and a context.Context, and connects immediately.

Until 3.3.1 this package was called experimental. If you upgrade, change the import to .../v3/tradingws and experimental. to tradingws., see the migration note.

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 is required to sign orders
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() // required: closes the socket and wakes every waiting request

Points to know:

  • It takes its defaults from Config (TradingWSDomain, TimeoutMs, UserAgent, PrivateKey); only wss:// URLs are accepted.
  • There is no automatic reconnect: call ws.Client().Connect(ctx) and subscribe again.
  • Methods, options, behaviour and examples: Trading WebSocket. Settings: Configuration.

Full example

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)
    }

    // Market data
    data := ssi.NewData(auth)
    ohlc, err := data.MarketData.GetOHLC1Minute("SSI")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(ohlc)

    // Trading
    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 seconds
    s.Wait(&timeoutMs)
}

Notes

  • Go SDK does not have separate async variants — use goroutines for concurrency.
  • All clients share the HTTP connection via Auth.
  • Always use defer auth.Close() and defer s.Disconnect() for cleanup (and defer ws.Close() for the Trading WebSocket client).
  • ssi.NewClient is deprecated: use ssi.NewAuth plus NewData / NewTrading / NewStream.
  • Read secrets and the OTP from environment variables or a secret store; do not hard-code them or log tokens.
  • Classify errors with errors.As against the types in the ssisdk package: *ssisdk.AuthenticationError (auth), *ssisdk.WebSocketError (stream), *ssisdk.TradingWSError (Trading WebSocket), *ssisdk.APIError (HTTP business errors). See Error Handling.

On this page