Client Classes
AuthClient, DataClient, TradingClient, StreamClient — khởi tạo và sử dụng
SDK cung cấp 4 client chuyên biệt. Tất cả method API đều là async và trả về Task/Task<T>.
AuthClient
Client gốc — quản lý REST client, xác thực, và token. Implement IDisposable. Tất cả client khác nhận AuthClient làm tham số.
using SsiSdk;
var config = new Config("YOUR_CLIENT_ID")
{
ApiKey = "YOUR_API_KEY",
ApiSecret = "YOUR_API_SECRET",
PrivateKey = "YOUR_PRIVATE_KEY",
};
using var auth = new AuthClient(config);
var token = await auth.AuthenticateAsync("YOUR_OTP");
Console.WriteLine($"Đã xác thực, token hết hạn lúc (epoch giây): {token.ExpiresAt}");Không in hay log
AccessToken: đây là credential.
Các method xác thực
// Yêu cầu gửi OTP (trả về OtpResponse { Message, TransactionId? })
OtpResponse otp = await auth.RequestOtpTypedAsync();
// RequestOtpAsync() trả về JsonElement thô
// Xác thực với OTP
var token = await auth.AuthenticateAsync("YOUR_OTP");
// Xác thực không cần OTP (chỉ dữ liệu thị trường)
var token = await auth.AuthenticateAsync();
// Làm mới token
var token = await auth.RefreshAsync();
// Đảm bảo xác thực (dùng token hiện có, refresh nếu hết hạn, hoặc xác thực lại)
var accessToken = await auth.EnsureAuthenticatedAsync("YOUR_OTP");AuthenticateAsync(otp?, transactionId?) là một lời gọi duy nhất. Nếu server báo Smart OTP chưa được duyệt (HTTP 202, code 401114), method ném SmartOtpPendingException. Truyền đồng thời otp và transactionId ném AuthenticationException. Với Token, ExpiresAt và RefreshTokenExpiresAt là epoch giây; 0 nghĩa là không rõ hoặc đã hết hạn.
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 RequestOtpTypedAsync() để gửi yêu cầu và lấy TransactionId, rồi truyền vào EnsureAuthenticatedAsync() — SDK tự poll cho đến khi user bấm duyệt hoặc hết thời gian chờ.
// Bước 1: gửi yêu cầu duyệt, nhận TransactionId
var otp = await auth.RequestOtpTypedAsync();
// Bước 2: SDK poll cho đến khi user bấm duyệt trên app Smart OTP
var accessToken = await auth.EnsureAuthenticatedAsync(
transactionId: otp.TransactionId,
pollIntervalMs: 5000, // mặc định Config.OtpPollIntervalMs
pollMaxRetries: 12); // mặc định suy ra từ Config.OtpPollMaxWaitMsThứ tự EnsureAuthenticatedAsync: dùng token còn hạn, nếu không thì refresh, nếu không thì xác thực bằng OTP, và với transactionId thì poll Smart OTP. Trong lúc chờ, server trả HTTP 202 + code 401114 ("Push-approval is pending") — SDK tự poll. Các trường hợp lỗi:
| Tình huống | Exception |
|---|---|
| Hết số lần poll mà user chưa duyệt | SmartOtpPendingException |
User từ chối hoặc request hết hạn (401113, 401115, 401116) | SmartOtpRejectedException (kết thúc, không poll tiếp) |
Token đã hết hạn, không refresh được và không có otp/transactionId mới | ReauthenticationRequiredException |
Chưa có token và không có otp/transactionId | AuthenticationException |
pollMaxRetries mặc định suy ra từ Config.OtpPollMaxWaitMs / Config.OtpPollIntervalMs (mặc định 60000 / 5000, tức 12 lần). Xem Xử lý lỗi.
Trạng thái token
auth.AccessToken; // access token hiện tại (string, rỗng nếu chưa có)
auth.TokenManager.Token; // đối tượng Token đầy đủ
auth.TokenManager.IsTokenExpired; // true/false
auth.SetToken(token); // thiết lập token (cache); cũng có auth.TokenManager.SetToken
auth.RateLimit; // RateLimitInfo từ response gần nhấtDataClient
Client dữ liệu thị trường. Không cần OTP — chỉ cần auth.AuthenticateAsync().
using SsiSdk;
using var auth = new AuthClient(config);
await auth.AuthenticateAsync();
var data = new DataClient(auth);
var ohlc = await data.MarketData.GetOhlc1MinuteAsync("SSI");
var indexes = await data.MarketData.GetIndexesAsync();
var info = await data.MarketData.GetSecuritiesInfoAsync("SSI");Service: data.MarketData (MarketDataService) — OHLC, chỉ số, chứng khoán.
TradingClient
Client giao dịch, tài khoản, và danh mục. Cần OTP.
using SsiSdk;
using var auth = new AuthClient(config);
await auth.AuthenticateAsync("YOUR_OTP");
var trading = new TradingClient(auth);
// Tài khoản
var accounts = await trading.Account.GetAccountInfoAsync();
// Danh mục
var balance = await trading.Portfolio.GetEquityBalanceAsync("YOUR_ACCOUNT_NO");
var positions = await trading.Portfolio.GetEquityPositionsAsync("YOUR_ACCOUNT_NO");
var orders = await trading.Portfolio.GetTodayOrdersAsync("YOUR_ACCOUNT_NO");
// Giao dịch
var result = await trading.Trading.PlaceLimitOrderAsync(
"YOUR_ACCOUNT_NO", "SSI", OrderSide.Buy, 100, 66000m);Services:
trading.Account(AccountService) — thông tin tài khoản.trading.Portfolio(PortfolioService) — số dư, vị thế, sổ lệnh, PPMMR.trading.Trading(TradingService) — đặt/sửa/huỷ lệnh, lệnh hàng loạt, sức mua/bán, lệnh điều kiện (FCO).
Lệnh giao dịch qua WebSocket riêng không đi qua TradingClient: xem Trading WebSocket.
StreamClient
Client streaming realtime qua WebSocket. Implement IDisposable. Cần OTP.
using SsiSdk;
using SsiSdk.Models;
using var auth = new AuthClient(config);
await auth.AuthenticateAsync("YOUR_OTP");
using var stream = new StreamClient(auth);
// Đăng ký callback
stream.Streaming.SetOnData(msg =>
{
switch (msg)
{
case TradeMessage trade:
Console.WriteLine($"[TRADE] {trade.Symbol} | {trade.Price} | {trade.Quantity}");
break;
case QuoteMessage quote:
Console.WriteLine($"[QUOTE] {quote.Symbol}");
break;
}
});
stream.Streaming.SetOnTrading(msg =>
{
if (msg is OrderStatusMessage order)
Console.WriteLine($"[ORDER] {order.Symbol} {order.Side} | {order.Status}");
});
// Kết nối và subscribe
await stream.ConnectAsync();
await stream.Streaming.SubscribeSymbolAsync(["SSI", "HPG"]);
await stream.Streaming.SubscribeOrderStatusAsync();
await stream.WaitAsync();Service: stream.Streaming (StreamingService) — subscribe/unsubscribe dữ liệu realtime.
Callbacks:
| Method | Callback | Mô tả |
|---|---|---|
SetOnData | Action<object>? | Nhận dữ liệu thị trường (dùng pattern matching để xử lý) |
SetOnTrading | Action<object>? | Nhận sự kiện giao dịch (trạng thái lệnh, danh mục) |
SetOnHeartbeat | Action<HeartbeatMessage>? | Nhận heartbeat |
Trạng thái kết nối & dọn dẹp:
stream.IsConnected; // true/false
stream.LastError; // Exception? gần nhất
stream.ReconnectCount; // số lần đã reconnect
stream.OnConnection = e => Console.WriteLine($"{e.State} (reconnect #{e.ReconnectCount})");
stream.Streaming.StopPingLoop();
await stream.DisconnectAsync(); // gọi khi không dùng using; cũng có Disconnect() đồng bộSocket tự reconnect sau khi rớt (Config.AutoReconnect, mặc định bật) và gửi lại các subscription đã ghi nhận. Chi tiết xem Streaming.
Ví dụ đầy đủ
using SsiSdk;
using SsiSdk.Models;
var config = new Config("YOUR_CLIENT_ID")
{
ApiKey = "YOUR_API_KEY",
ApiSecret = "YOUR_API_SECRET",
PrivateKey = "YOUR_PRIVATE_KEY",
};
using var auth = new AuthClient(config);
await auth.AuthenticateAsync("YOUR_OTP");
// Dữ liệu thị trường
var data = new DataClient(auth);
var ohlc = await data.MarketData.GetOhlc1MinuteAsync("SSI");
Console.WriteLine(ohlc);
// Giao dịch
var trading = new TradingClient(auth);
var accounts = await trading.Account.GetAccountInfoAsync();
Console.WriteLine(accounts);
// Streaming
using var stream = new StreamClient(auth);
stream.Streaming.SetOnData(msg =>
{
if (msg is TradeMessage trade)
Console.WriteLine($"[TRADE] {trade.Symbol} | {trade.Price} | {trade.Quantity}");
});
await stream.ConnectAsync();
await stream.Streaming.SubscribeSymbolAsync(["SSI", "HPG"]);
await stream.Streaming.SubscribeOrderStatusAsync();
await stream.WaitAsync(timeoutMs: 30000);Ghi chú
- Tất cả method API đều là
async— dùngawait. AuthClientvàStreamClientimplementIDisposable— luôn dùngusing/using var.- Tất cả client dùng chung HTTP connection qua
AuthClient. - Lỗi thường gặp:
AuthenticationException(xác thực; gồmSmartOtpPendingException,SmartOtpRejectedException,ReauthenticationRequiredException),WebSocketException(stream),ApiException(lỗi HTTP),RateLimitException(429),ValidationException(đầu vào sai, chưa gửi request). Xem Xử lý lỗi. auth.RateLimitgiữ thông tin rate limit của response gần nhất.