Go SDK
Account Management
Account balances, positions, and margin management with the Go SDK
Account Management
The Go SDK provides comprehensive account management including balance queries, position tracking, margin information, and settlement status.
Account Types
// Balance represents account balance for an asset
type Balance struct {
Asset string `json:"asset"` // Asset symbol (e.g., "BTC", "USD")
Available float64 `json:"available"` // Available for trading
Locked float64 `json:"locked"` // Locked in open orders
Total float64 `json:"total"` // Total balance
}
// Position represents a trading position
type Position struct {
Symbol string `json:"symbol"` // Trading pair
Size float64 `json:"size"` // Position size (negative = short)
EntryPrice float64 `json:"entryPrice"` // Average entry price
MarkPrice float64 `json:"markPrice"` // Current mark price
PnL float64 `json:"pnl"` // Realized P&L
Margin float64 `json:"margin"` // Position margin
}
// MarginInfo represents margin account information
type MarginInfo struct {
UserID string `json:"user_id"`
InitialMargin float64 `json:"initial_margin"`
MaintenanceMargin float64 `json:"maintenance_margin"`
MarginRatio float64 `json:"margin_ratio"`
FreeMargin float64 `json:"free_margin"`
MarginLevel float64 `json:"margin_level"`
}Querying Balances
Get All Balances
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/luxfi/dex/sdk/go/client"
)
func main() {
c, err := client.NewClient(
client.WithJSONRPCURL("http://localhost:8080"),
client.WithAPIKey("your-api-key"),
)
if err != nil {
log.Fatal(err)
}
defer c.Disconnect()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Get account balances (example implementation)
balances := []client.Balance{
{Asset: "BTC", Available: 1.5, Locked: 0.1, Total: 1.6},
{Asset: "ETH", Available: 10.0, Locked: 2.0, Total: 12.0},
{Asset: "USD", Available: 50000, Locked: 5000, Total: 55000},
{Asset: "LUX", Available: 1000, Locked: 0, Total: 1000},
}
fmt.Println("Account Balances")
fmt.Println("================")
fmt.Printf("%-8s %15s %15s %15s %10s\n",
"Asset", "Available", "Locked", "Total", "Util %")
fmt.Println("------------------------------------------------------------")
for _, bal := range balances {
utilization := bal.Utilization() * 100
fmt.Printf("%-8s %15.8f %15.8f %15.8f %9.2f%%\n",
bal.Asset, bal.Available, bal.Locked, bal.Total, utilization)
}
}Balance with Real-Time Updates
package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"sync"
"syscall"
"github.com/luxfi/dex/sdk/go/client"
)
type BalanceTracker struct {
mu sync.RWMutex
balances map[string]*client.Balance
}
func NewBalanceTracker() *BalanceTracker {
return &BalanceTracker{
balances: make(map[string]*client.Balance),
}
}
func (bt *BalanceTracker) Update(balance *client.Balance) {
bt.mu.Lock()
defer bt.mu.Unlock()
bt.balances[balance.Asset] = balance
}
func (bt *BalanceTracker) Get(asset string) *client.Balance {
bt.mu.RLock()
defer bt.mu.RUnlock()
return bt.balances[asset]
}
func (bt *BalanceTracker) GetAll() map[string]*client.Balance {
bt.mu.RLock()
defer bt.mu.RUnlock()
result := make(map[string]*client.Balance, len(bt.balances))
for k, v := range bt.balances {
result[k] = v
}
return result
}
func (bt *BalanceTracker) TotalUSDValue(prices map[string]float64) float64 {
bt.mu.RLock()
defer bt.mu.RUnlock()
var total float64
for asset, bal := range bt.balances {
if asset == "USD" {
total += bal.Total
} else if price, ok := prices[asset]; ok {
total += bal.Total * price
}
}
return total
}
func main() {
c, err := client.NewClient(
client.WithWebSocketURL("ws://localhost:8081"),
client.WithAPIKey("your-api-key"),
)
if err != nil {
log.Fatal(err)
}
defer c.Disconnect()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := c.ConnectWebSocket(ctx); err != nil {
log.Fatal("Failed to connect WebSocket:", err)
}
tracker := NewBalanceTracker()
userID := "trader-001"
// Subscribe to balance updates
err = c.Subscribe(fmt.Sprintf("user:balances:%s", userID), func(data interface{}) {
if balData, ok := data.(map[string]interface{}); ok {
balance := &client.Balance{
Asset: balData["asset"].(string),
Available: balData["available"].(float64),
Locked: balData["locked"].(float64),
Total: balData["total"].(float64),
}
tracker.Update(balance)
fmt.Printf("[Balance Update] %s: Available=%.8f, Locked=%.8f\n",
balance.Asset, balance.Available, balance.Locked)
}
})
if err != nil {
log.Fatal("Failed to subscribe:", err)
}
fmt.Println("Listening for balance updates...")
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
// Print final balances
fmt.Println("\nFinal Balances:")
for _, bal := range tracker.GetAll() {
fmt.Printf(" %s: %.8f\n", bal.Asset, bal.Total)
}
}Position Management
Get Open Positions
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/luxfi/dex/sdk/go/client"
)
func main() {
c, err := client.NewClient(
client.WithJSONRPCURL("http://localhost:8080"),
client.WithAPIKey("your-api-key"),
)
if err != nil {
log.Fatal(err)
}
defer c.Disconnect()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Example positions (would come from API)
positions := []client.Position{
{
Symbol: "BTC-USD",
Size: 0.5,
EntryPrice: 48000,
MarkPrice: 50000,
PnL: 1000,
Margin: 4800,
},
{
Symbol: "ETH-USD",
Size: -5.0, // Short position
EntryPrice: 3200,
MarkPrice: 3100,
PnL: 500,
Margin: 3200,
},
}
fmt.Println("Open Positions")
fmt.Println("==============")
var totalPnL, totalMargin float64
for _, pos := range positions {
direction := "LONG"
if pos.Size < 0 {
direction = "SHORT"
}
unrealizedPnL := pos.UnrealizedPnL()
pnlPct := pos.PnLPercentage()
fmt.Printf("\n%s (%s)\n", pos.Symbol, direction)
fmt.Printf(" Size: %.4f\n", pos.Size)
fmt.Printf(" Entry: $%.2f\n", pos.EntryPrice)
fmt.Printf(" Mark: $%.2f\n", pos.MarkPrice)
fmt.Printf(" Unrealized: $%.2f (%.2f%%)\n", unrealizedPnL, pnlPct)
fmt.Printf(" Realized: $%.2f\n", pos.PnL)
fmt.Printf(" Margin: $%.2f\n", pos.Margin)
totalPnL += unrealizedPnL + pos.PnL
totalMargin += pos.Margin
}
fmt.Printf("\n-----------------\n")
fmt.Printf("Total P&L: $%.2f\n", totalPnL)
fmt.Printf("Total Margin: $%.2f\n", totalMargin)
}Position Monitoring
package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"sync"
"syscall"
"time"
"github.com/luxfi/dex/sdk/go/client"
)
type PositionManager struct {
mu sync.RWMutex
positions map[string]*client.Position
alerts chan PositionAlert
}
type PositionAlert struct {
Symbol string
Type string
Message string
}
func NewPositionManager() *PositionManager {
return &PositionManager{
positions: make(map[string]*client.Position),
alerts: make(chan PositionAlert, 100),
}
}
func (pm *PositionManager) Update(pos *client.Position) {
pm.mu.Lock()
defer pm.mu.Unlock()
oldPos := pm.positions[pos.Symbol]
pm.positions[pos.Symbol] = pos
// Check for alerts
pm.checkAlerts(oldPos, pos)
}
func (pm *PositionManager) checkAlerts(old, new *client.Position) {
// New position opened
if old == nil && new.Size != 0 {
pm.alerts <- PositionAlert{
Symbol: new.Symbol,
Type: "OPENED",
Message: fmt.Sprintf("New position: %.4f @ %.2f", new.Size, new.EntryPrice),
}
return
}
// Position closed
if old != nil && old.Size != 0 && new.Size == 0 {
pm.alerts <- PositionAlert{
Symbol: new.Symbol,
Type: "CLOSED",
Message: fmt.Sprintf("Position closed, P&L: $%.2f", new.PnL),
}
return
}
// Large P&L change
if old != nil {
oldPnL := old.UnrealizedPnL()
newPnL := new.UnrealizedPnL()
pnlChange := newPnL - oldPnL
if abs(pnlChange) > 100 { // $100 threshold
pm.alerts <- PositionAlert{
Symbol: new.Symbol,
Type: "PNL_CHANGE",
Message: fmt.Sprintf("P&L change: $%.2f -> $%.2f", oldPnL, newPnL),
}
}
}
// Liquidation warning (margin ratio > 80%)
if new.Margin > 0 {
marginRatio := abs(new.UnrealizedPnL()) / new.Margin
if marginRatio > 0.8 {
pm.alerts <- PositionAlert{
Symbol: new.Symbol,
Type: "LIQUIDATION_WARNING",
Message: fmt.Sprintf("High margin usage: %.1f%%", marginRatio*100),
}
}
}
}
func (pm *PositionManager) Alerts() <-chan PositionAlert {
return pm.alerts
}
func (pm *PositionManager) GetPosition(symbol string) *client.Position {
pm.mu.RLock()
defer pm.mu.RUnlock()
return pm.positions[symbol]
}
func (pm *PositionManager) TotalUnrealizedPnL() float64 {
pm.mu.RLock()
defer pm.mu.RUnlock()
var total float64
for _, pos := range pm.positions {
total += pos.UnrealizedPnL()
}
return total
}
func abs(x float64) float64 {
if x < 0 {
return -x
}
return x
}
func main() {
c, err := client.NewClient(
client.WithWebSocketURL("ws://localhost:8081"),
client.WithAPIKey("your-api-key"),
)
if err != nil {
log.Fatal(err)
}
defer c.Disconnect()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := c.ConnectWebSocket(ctx); err != nil {
log.Fatal("Failed to connect:", err)
}
pm := NewPositionManager()
userID := "trader-001"
// Subscribe to position updates
err = c.Subscribe(fmt.Sprintf("user:positions:%s", userID), func(data interface{}) {
if posData, ok := data.(map[string]interface{}); ok {
pos := &client.Position{
Symbol: posData["symbol"].(string),
Size: posData["size"].(float64),
EntryPrice: posData["entryPrice"].(float64),
MarkPrice: posData["markPrice"].(float64),
PnL: posData["pnl"].(float64),
Margin: posData["margin"].(float64),
}
pm.Update(pos)
}
})
if err != nil {
log.Fatal("Failed to subscribe:", err)
}
// Handle alerts
go func() {
for alert := range pm.Alerts() {
fmt.Printf("[ALERT] %s - %s: %s\n",
alert.Symbol, alert.Type, alert.Message)
}
}()
// Print P&L periodically
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
for {
select {
case <-ticker.C:
fmt.Printf("Total Unrealized P&L: $%.2f\n", pm.TotalUnrealizedPnL())
case <-sigCh:
fmt.Println("\nShutting down...")
return
}
}
}Margin Information
Query Margin Status
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/luxfi/dex/sdk/go/client"
)
func main() {
c, err := client.NewClient(
client.WithJSONRPCURL("http://localhost:8080"),
client.WithAPIKey("your-api-key"),
)
if err != nil {
log.Fatal(err)
}
defer c.Disconnect()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
userID := "trader-001"
marginInfo, err := c.GetMarginInfo(ctx, userID)
if err != nil {
log.Fatal("Failed to get margin info:", err)
}
fmt.Println("Margin Account Status")
fmt.Println("=====================")
fmt.Printf("User ID: %s\n", marginInfo.UserID)
fmt.Printf("Initial Margin: $%.2f\n", marginInfo.InitialMargin)
fmt.Printf("Maintenance Margin: $%.2f\n", marginInfo.MaintenanceMargin)
fmt.Printf("Margin Ratio: %.2f%%\n", marginInfo.MarginRatio*100)
fmt.Printf("Free Margin: $%.2f\n", marginInfo.FreeMargin)
fmt.Printf("Margin Level: %.2f\n", marginInfo.MarginLevel)
// Risk assessment
fmt.Println("\nRisk Assessment:")
if marginInfo.MarginRatio < 0.5 {
fmt.Println(" Status: HEALTHY")
} else if marginInfo.MarginRatio < 0.8 {
fmt.Println(" Status: WARNING - Consider reducing positions")
} else {
fmt.Println(" Status: CRITICAL - Liquidation risk!")
}
}Margin Call Monitoring
package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"time"
"github.com/luxfi/dex/sdk/go/client"
)
type MarginMonitor struct {
client *client.Client
userID string
warningLevel float64
criticalLevel float64
checkInterval time.Duration
}
func NewMarginMonitor(c *client.Client, userID string) *MarginMonitor {
return &MarginMonitor{
client: c,
userID: userID,
warningLevel: 0.7, // 70% margin usage
criticalLevel: 0.9, // 90% margin usage
checkInterval: 5 * time.Second,
}
}
func (mm *MarginMonitor) Start(ctx context.Context) {
ticker := time.NewTicker(mm.checkInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
mm.checkMargin(ctx)
case <-ctx.Done():
return
}
}
}
func (mm *MarginMonitor) checkMargin(ctx context.Context) {
checkCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
marginInfo, err := mm.client.GetMarginInfo(checkCtx, mm.userID)
if err != nil {
log.Printf("Failed to get margin info: %v", err)
return
}
ratio := marginInfo.MarginRatio
if ratio >= mm.criticalLevel {
fmt.Printf("[CRITICAL] Margin ratio: %.1f%% - LIQUIDATION IMMINENT!\n", ratio*100)
// Could trigger automatic position reduction here
} else if ratio >= mm.warningLevel {
fmt.Printf("[WARNING] Margin ratio: %.1f%% - Consider reducing exposure\n", ratio*100)
}
}
func main() {
c, err := client.NewClient(
client.WithJSONRPCURL("http://localhost:8080"),
client.WithAPIKey("your-api-key"),
)
if err != nil {
log.Fatal(err)
}
defer c.Disconnect()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
monitor := NewMarginMonitor(c, "trader-001")
go monitor.Start(ctx)
fmt.Println("Margin monitoring started...")
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
fmt.Println("Shutting down margin monitor...")
}Liquidation Monitoring
Subscribe to Liquidations
package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"github.com/luxfi/dex/sdk/go/client"
)
func main() {
c, err := client.NewClient(
client.WithWebSocketURL("ws://localhost:8081"),
)
if err != nil {
log.Fatal(err)
}
defer c.Disconnect()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := c.ConnectWebSocket(ctx); err != nil {
log.Fatal("Failed to connect:", err)
}
// Subscribe to all liquidations (market-wide)
err = c.SubscribeToLiquidations(func(liq *client.LiquidationInfo) {
fmt.Printf("\n[LIQUIDATION] %s\n", liq.Symbol)
fmt.Printf(" Size: %.4f\n", liq.Size)
fmt.Printf(" Liquidation Price: $%.2f\n", liq.LiquidationPrice)
fmt.Printf(" Mark Price: $%.2f\n", liq.MarkPrice)
fmt.Printf(" Status: %s\n", liq.Status)
})
if err != nil {
log.Fatal("Failed to subscribe:", err)
}
fmt.Println("Monitoring liquidations...")
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
}Settlement Tracking
Monitor Settlement Batches
package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"github.com/luxfi/dex/sdk/go/client"
)
func main() {
c, err := client.NewClient(
client.WithWebSocketURL("ws://localhost:8081"),
)
if err != nil {
log.Fatal(err)
}
defer c.Disconnect()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := c.ConnectWebSocket(ctx); err != nil {
log.Fatal("Failed to connect:", err)
}
// Subscribe to settlement updates
err = c.SubscribeToSettlements(func(batch *client.SettlementBatch) {
fmt.Printf("\n[SETTLEMENT] Batch #%d\n", batch.BatchID)
fmt.Printf(" Orders: %d\n", len(batch.Orders))
fmt.Printf(" Status: %s\n", batch.Status)
if batch.TxHash != "" {
fmt.Printf(" Tx Hash: %s\n", batch.TxHash)
fmt.Printf(" Gas Used: %d\n", batch.GasUsed)
}
fmt.Printf(" Timestamp: %s\n", batch.Timestamp)
})
if err != nil {
log.Fatal("Failed to subscribe:", err)
}
fmt.Println("Monitoring settlements...")
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
}Query Settlement Status
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/luxfi/dex/sdk/go/client"
)
func main() {
c, err := client.NewClient(
client.WithJSONRPCURL("http://localhost:8080"),
)
if err != nil {
log.Fatal(err)
}
defer c.Disconnect()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
batchID := uint64(12345)
batch, err := c.GetSettlementBatch(ctx, batchID)
if err != nil {
log.Fatal("Failed to get settlement batch:", err)
}
fmt.Printf("Settlement Batch #%d\n", batch.BatchID)
fmt.Println("====================")
fmt.Printf("Status: %s\n", batch.Status)
fmt.Printf("Orders: %v\n", batch.Orders)
fmt.Printf("Timestamp: %s\n", batch.Timestamp)
if batch.TxHash != "" {
fmt.Printf("\nOn-chain Settlement:\n")
fmt.Printf(" Transaction: %s\n", batch.TxHash)
fmt.Printf(" Gas Used: %d\n", batch.GasUsed)
}
}Next Steps
- WebSocket - Real-time streaming patterns
- Error Handling - Error codes and retry strategies
- Orders - Order management