Integración API

¿Cómo llamar a la API de Binance con Go? Ejemplos con net/http y go-binance

Tutorial completo de la API de Binance en lenguaje Go: firma con crypto/hmac, encapsulación de solicitudes net/http, biblioteca oficial de la comunidad go-binance, suscripciones en tiempo real con gorilla/websocket y código práctico para órdenes concurrentes con goroutines.

El acceso a la API de Binance con el lenguaje Go combina las ventajas de un rendimiento extremadamente alto y un despliegue sin dependencias. Un único archivo binario ejecutándose en un servidor Linux presenta una latencia entre un 30% y 50% menor que Python o Node.js. La solución estándar consiste en usar la biblioteca estándar crypto/hmac para generar firmas y net/http para enviar solicitudes, o utilizar directamente la biblioteca comunitaria github.com/adshao/go-binance/v2. Este artículo presenta el código completo para cuatro módulos: encapsulación nativa, uso del SDK, suscripción WebSocket y colocación de órdenes concurrentes. Si aún no se ha registrado en Binance, complete primero el KYC en el sitio oficial de Binance; los nuevos usuarios pueden registrarse gratis aquí.

I. Comparativa de SDK en Go

Biblioteca Ruta Estrellas en GitHub Características
adshao/go-binance github.com/adshao/go-binance/v2 3.2k Preferida por la comunidad, soporte total para Spot + Futures
go-numb/go-binance github.com/go-numb/go-binance 50+ Ligera, enfocada en WebSocket
Nativa net/http Biblioteca estándar Control total, cero dependencias

Recomendación: Para un desarrollo rápido, elija adshao/go-binance; para personalización extrema o escenarios HFT, use net/http nativo.

II. Implementación de firma con net/http nativo

1. Inicialización del proyecto

mkdir binance-go && cd binance-go
go mod init binance-go
go get github.com/joho/godotenv

Archivo .env:

BINANCE_API_KEY=tu_key
BINANCE_SECRET_KEY=tu_secret

2. Firma y solicitud principal

client.go:

package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
    "strconv"
    "time"
)

const BaseURL = "https://api.binance.com"

type Client struct {
    APIKey    string
    SecretKey string
    HTTPClient *http.Client
}

func NewClient() *Client {
    return &Client{
        APIKey:    os.Getenv("BINANCE_API_KEY"),
        SecretKey: os.Getenv("BINANCE_SECRET_KEY"),
        HTTPClient: &http.Client{
            Timeout: 10 * time.Second,
            Transport: &http.Transport{
                MaxIdleConns:        100,
                MaxIdleConnsPerHost: 100,
                IdleConnTimeout:     90 * time.Second,
            },
        },
    }
}

func (c *Client) sign(payload string) string {
    h := hmac.New(sha256.New, []byte(c.SecretKey))
    h.Write([]byte(payload))
    return hex.EncodeToString(h.Sum(nil))
}

func (c *Client) Request(method, path string, params url.Values, signed bool) ([]byte, error) {
    if params == nil {
        params = url.Values{}
    }
    if signed {
        params.Set("timestamp", strconv.FormatInt(time.Now().UnixMilli(), 10))
        params.Set("recvWindow", "5000")
        query := params.Encode()
        params.Set("signature", c.sign(query))
    }

    fullURL := BaseURL + path
    var req *http.Request
    var err error

    if method == "GET" || method == "DELETE" {
        fullURL = fullURL + "?" + params.Encode()
        req, err = http.NewRequest(method, fullURL, nil)
    } else {
        req, err = http.NewRequest(method, fullURL, nil)
        req.URL.RawQuery = params.Encode()
    }
    if err != nil {
        return nil, err
    }

    if signed {
        req.Header.Set("X-MBX-APIKEY", c.APIKey)
    }

    resp, err := c.HTTPClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }
    if resp.StatusCode >= 400 {
        return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body))
    }
    return body, nil
}

type Balance struct {
    Asset  string `json:"asset"`
    Free   string `json:"free"`
    Locked string `json:"locked"`
}

type Account struct {
    CanTrade bool      `json:"canTrade"`
    Balances []Balance `json:"balances"`
}

func (c *Client) GetAccount() (*Account, error) {
    body, err := c.Request("GET", "/api/v3/account", nil, true)
    if err != nil {
        return nil, err
    }
    var acc Account
    if err := json.Unmarshal(body, &acc); err != nil {
        return nil, err
    }
    return &acc, nil
}

type Ticker struct {
    Symbol string `json:"symbol"`
    Price  string `json:"price"`
}

func (c *Client) GetPrice(symbol string) (float64, error) {
    params := url.Values{}
    params.Set("symbol", symbol)
    body, err := c.Request("GET", "/api/v3/ticker/price", params, false)
    if err != nil {
        return 0, err
    }
    var t Ticker
    if err := json.Unmarshal(body, &t); err != nil {
        return 0, err
    }
    return strconv.ParseFloat(t.Price, 64)
}

type Order struct {
    Symbol       string `json:"symbol"`
    OrderID      int64  `json:"orderId"`
    ClientOrderID string `json:"clientOrderId"`
    Status       string `json:"status"`
    ExecutedQty  string `json:"executedQty"`
}

func (c *Client) PlaceLimitOrder(symbol, side, quantity, price string) (*Order, error) {
    params := url.Values{}
    params.Set("symbol", symbol)
    params.Set("side", side)
    params.Set("type", "LIMIT")
    params.Set("timeInForce", "GTC")
    params.Set("quantity", quantity)
    params.Set("price", price)

    body, err := c.Request("POST", "/api/v3/order", params, true)
    if err != nil {
        return nil, err
    }
    var o Order
    if err := json.Unmarshal(body, &o); err != nil {
        return nil, err
    }
    return &o, nil
}

3. Programa principal

main.go:

package main

import (
    "fmt"
    "log"
    "strconv"

    "github.com/joho/godotenv"
)

func main() {
    if err := godotenv.Load(); err != nil {
        log.Println("No se encontró .env, usando variables de entorno")
    }

    c := NewClient()

    // Consultar cuenta
    account, err := c.GetAccount()
    if err != nil {
        log.Fatalln("Error al consultar cuenta:", err)
    }
    fmt.Printf("Puede tradear: %v\n", account.CanTrade)
    for _, b := range account.Balances {
        free, _ := strconv.ParseFloat(b.Free, 64)
        if free > 0 {
            fmt.Printf("  %s: %s (Bloqueado %s)\n", b.Asset, b.Free, b.Locked)
        }
    }

    // Consultar precio
    price, _ := c.GetPrice("BTCUSDT")
    fmt.Printf("Último precio de BTC: %.2f\n", price)

    // Colocar orden (a un -10% del precio de mercado)
    testPrice := fmt.Sprintf("%.2f", price*0.9)
    order, err := c.PlaceLimitOrder("BTCUSDT", "BUY", "0.001", testPrice)
    if err != nil {
        log.Fatalln("Error al colocar orden:", err)
    }
    fmt.Printf("Orden %d creada, estado %s\n", order.OrderID, order.Status)
}

Para ejecutar:

go run *.go

III. Biblioteca comunitaria go-binance

1. Instalación

go get github.com/adshao/go-binance/v2

2. Ejemplo para Spot

package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/adshao/go-binance/v2"
)

func main() {
    client := binance.NewClient(
        os.Getenv("BINANCE_API_KEY"),
        os.Getenv("BINANCE_SECRET_KEY"),
    )
    ctx := context.Background()

    // Consultar saldos
    account, err := client.NewGetAccountService().Do(ctx)
    if err != nil {
        log.Fatal(err)
    }
    for _, b := range account.Balances {
        fmt.Printf("%s: %s\n", b.Asset, b.Free)
    }

    // Consultar precio
    prices, err := client.NewListPricesService().Symbol("BTCUSDT").Do(ctx)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(prices[0].Price)

    // Orden límite de compra
    order, err := client.NewCreateOrderService().
        Symbol("BTCUSDT").
        Side(binance.SideTypeBuy).
        Type(binance.OrderTypeLimit).
        TimeInForce(binance.TimeInForceTypeGTC).
        Quantity("0.001").
        Price("60000.00").
        Do(ctx)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Orden %d\n", order.OrderID)

    // K-lines
    klines, _ := client.NewKlinesService().
        Symbol("BTCUSDT").
        Interval("1h").
        Limit(100).
        Do(ctx)
    fmt.Printf("Obtenidas %d K-lines\n", len(klines))
}

3. Ejemplo para Futures

futuresClient := binance.NewFuturesClient(apiKey, secret)

// Establecer apalancamiento
_, err := futuresClient.NewChangeLeverageService().
    Symbol("BTCUSDT").Leverage(10).Do(ctx)

// Abrir posición larga (Long)
futuresOrder, err := futuresClient.NewCreateOrderService().
    Symbol("BTCUSDT").
    Side(binance.SideTypeBuy).
    Type(binance.OrderTypeLimit).
    TimeInForce(binance.TimeInForceTypeGTC).
    Quantity("0.01").
    Price("60000").
    PositionSide(binance.PositionSideTypeLong).
    Do(ctx)

// Consultar riesgo de posición
positions, _ := futuresClient.NewGetPositionRiskService().Do(ctx)
for _, p := range positions {
    if p.PositionAmt != "0" {
        fmt.Printf("%s Posición %s @ %s\n", p.Symbol, p.PositionAmt, p.EntryPrice)
    }
}

IV. Suscripción WebSocket (gorilla/websocket)

go get github.com/gorilla/websocket
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "time"

    "github.com/gorilla/websocket"
)

type TickerEvent struct {
    EventType string `json:"e"`
    EventTime int64  `json:"E"`
    Symbol    string `json:"s"`
    Close     string `json:"c"`
    Change    string `json:"P"`
    Volume    string `json:"v"`
}

func main() {
    url := "wss://stream.binance.com:9443/stream?streams=btcusdt@ticker/ethusdt@ticker"

    for {
        conn, _, err := websocket.DefaultDialer.Dial(url, nil)
        if err != nil {
            log.Println("Fallo al conectar:", err, ", reintentando en 5s")
            time.Sleep(5 * time.Second)
            continue
        }
        log.Println("WS Conectado")

        // Goroutine de latido (heartbeat)
        stop := make(chan struct{})
        go func() {
            ticker := time.NewTicker(3 * time.Minute)
            defer ticker.Stop()
            for {
                select {
                case <-ticker.C:
                    conn.WriteMessage(websocket.PingMessage, nil)
                case <-stop:
                    return
                }
            }
        }()

        for {
            _, message, err := conn.ReadMessage()
            if err != nil {
                log.Println("Error de lectura:", err)
                close(stop)
                conn.Close()
                break
            }
            var combined struct {
                Stream string      `json:"stream"`
                Data   TickerEvent `json:"data"`
            }
            if err := json.Unmarshal(message, &combined); err != nil {
                continue
            }
            fmt.Printf("%s: %s (24h %s%%)\n",
                combined.Data.Symbol,
                combined.Data.Close,
                combined.Data.Change)
        }
    }
}

V. Órdenes concurrentes con goroutines

El modelo de concurrencia de Go es ideal para colocar múltiples órdenes simultáneamente:

func placeParallel(c *Client, orders []OrderSpec) []error {
    errs := make([]error, len(orders))
    done := make(chan int, len(orders))

    for i, spec := range orders {
        go func(idx int, s OrderSpec) {
            _, err := c.PlaceLimitOrder(s.Symbol, s.Side, s.Quantity, s.Price)
            errs[idx] = err
            done <- idx
        }(i, spec)
    }

    for range orders {
        <-done
    }
    return errs
}

type OrderSpec struct {
    Symbol   string
    Side     string
    Quantity string
    Price    string
}

// Uso
specs := []OrderSpec{
    {"BTCUSDT", "BUY", "0.001", "60000"},
    {"ETHUSDT", "BUY", "0.01", "3000"},
    {"BNBUSDT", "BUY", "0.1", "500"},
}
errors := placeParallel(client, specs)

Nota: En Spot de Binance el límite es de 100 órdenes cada 10 segundos; procure no exceder 50 concurrentes para evitar bloqueos por frecuencia.

VI. Preguntas frecuentes (FAQ)

P1: ¿Qué tamaño tiene el binario compilado de Go? ¿Tiene dependencias?

R: Un programa con todas las funciones de la API de Binance + WebSocket ocupa unos 8-15 MB tras compilar. Se ejecuta directamente en Linux o Docker sin dependencias de tiempo de ejecución (a diferencia de Node.js, que requiere llevar cientos de megas de node_modules).

P2: ¿Cuánta diferencia de rendimiento hay entre go-binance y net/http nativo?

R: La diferencia es mínima, go-binance es solo un envoltorio ligero. Una llamada REST simple tarda 5-8 ms; en pruebas de estrés con 1000 concurrencias, la latencia p99 < 30 ms. Comparado con los 80-100 ms de Python, Go ofrece una ventaja de más de 10 veces.

P3: ¿Cómo manejar elegantemente los errores devueltos por Binance?

R: El cuerpo del error de Binance es {"code": -1021, "msg": "..."}. Se recomienda definir un tipo BinanceError para un manejo uniforme:

type BinanceError struct {
    Code int    `json:"code"`
    Msg  string `json:"msg"`
}

func (e *BinanceError) Error() string {
    return fmt.Sprintf("Binance %d: %s", e.Code, e.Msg)
}

P4: ¿Es necesario manejar manualmente la desconexión de 24 horas en WebSocket con Go?

R: . Binance desconecta forzosamente todos los WS cada 24 horas y gorilla/websocket no incluye mecanismo de reconexión automática. Use el patrón de bucle for + Dial de la sección IV para reconectar sin interrupciones.

P5: ¿Cómo desplegar en Docker?

R: Use una construcción multietapa para obtener una imagen final de solo 20 MB:

FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o bot

FROM alpine:3.19
RUN apk add --no-cache ca-certificates tzdata
COPY --from=builder /app/bot /bot
ENTRYPOINT ["/bot"]

Tras explorar la solución en Go, regrese a la Navegación de categorías para consultar tutoriales de SDK en otros lenguajes dentro de la sección «Integración API».

Continuar explorando

¿Sigues con dudas sobre el uso de Binance? Vuelve a la página de categorías para encontrar otros tutoriales sobre el mismo tema.

Categorías

Tutoriales relacionados

¿Cómo solicitar la API de Binance? Guía para generar claves y firmas 2026-04-14 ¿Cómo usar la API Spot de Binance? Código listo para ejecutar desde cero hasta tu primera orden 2026-04-14 ¿Cuál es la diferencia entre la API de Binance Futures y Spot? Comparativa de endpoints, parámetros y pesos 2026-04-14 ¿Banearán mi IP de la API de Binance? Estrategias de límite de frecuencia y pesos explicadas 2026-04-14