Intégration API

Comment appeler l'API Binance en Go ? Exemples avec net/http et go-binance

Tutoriel complet sur l'API Binance en Go : signature crypto/hmac standard, encapsulation des requêtes net/http, bibliothèque communautaire officielle go-binance, abonnement en temps réel gorilla/websocket et code pratique pour le passage d'ordres simultanés avec goroutines.

L'accès à l'API Binance en langage Go combine deux avantages majeurs : une performance extrême et un déploiement sans dépendance. Un fichier binaire unique s'exécutant sur un serveur Linux offre une latence de 30 à 50 % inférieure à celle de Python ou Node.js. La solution standard consiste à utiliser la bibliothèque standard crypto/hmac pour générer les signatures et net/http pour envoyer les requêtes, ou à utiliser directement la bibliothèque communautaire github.com/adshao/go-binance/v2. Cet article fournit le code complet pour quatre modules : encapsulation native, utilisation du SDK, abonnement WebSocket et passage d'ordres concurrents. Pour ceux qui ne sont pas encore inscrits sur Binance, veuillez d'abord effectuer la vérification KYC sur le Site officiel de Binance. Les nouveaux utilisateurs peuvent s' inscrire gratuitement.

I. Comparaison des SDK Go

Bibliothèque Chemin Étoiles GitHub Caractéristiques
adshao/go-binance github.com/adshao/go-binance/v2 3.2k Choix numéro 1 de la communauté, support complet Spot + Futures
go-numb/go-binance github.com/go-numb/go-binance 50+ Léger, axé sur les WebSockets
net/http natif Bibliothèque standard Contrôle total, aucune dépendance

Recommandation : Choisissez adshao/go-binance pour un développement rapide ; utilisez net/http natif pour une personnalisation poussée ou des scénarios HFT (High Frequency Trading).

II. Implémentation de la signature avec net/http natif

1. Initialisation du projet

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

Fichier .env :

BINANCE_API_KEY=votre_cle
BINANCE_SECRET_KEY=votre_secret

2. Signature et requête de base

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. Programme principal

main.go :

package main

import (
    "fmt"
    "log"
    "strconv"

    "github.com/joho/godotenv"
)

func main() {
    if err := godotenv.Load(); err != nil {
        log.Println("Fichier .env non trouvé, utilisation des variables d'environnement")
    }

    c := NewClient()

    // Consulter le compte
    account, err := c.GetAccount()
    if err != nil {
        log.Fatalln("Échec de la consultation du compte :", err)
    }
    fmt.Printf("Peut trader : %v\n", account.CanTrade)
    for _, b := range account.Balances {
        free, _ := strconv.ParseFloat(b.Free, 64)
        if free > 0 {
            fmt.Printf("  %s : %s (Bloqué %s)\n", b.Asset, b.Free, b.Locked)
        }
    }

    // Consulter le prix
    price, _ := c.GetPrice("BTCUSDT")
    fmt.Printf("Dernier prix BTC : %.2f\n", price)

    // Placer un ordre (à -10% du prix du marché)
    testPrice := fmt.Sprintf("%.2f", price*0.9)
    order, err := c.PlaceLimitOrder("BTCUSDT", "BUY", "0.001", testPrice)
    if err != nil {
        log.Fatalln("Échec de l'ordre :", err)
    }
    fmt.Printf("Ordre %d créé, statut %s\n", order.OrderID, order.Status)
}

Exécution :

go run *.go

III. Bibliothèque communautaire go-binance

1. Installation

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

2. Exemple 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()

    // Consulter le solde
    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)
    }

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

    // Ordre d'achat limite
    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("Ordre %d\n", order.OrderID)

    // Klines
    klines, _ := client.NewKlinesService().
        Symbol("BTCUSDT").
        Interval("1h").
        Limit(100).
        Do(ctx)
    fmt.Printf("%d Klines récupérées\n", len(klines))
}

3. Exemple Futures

futuresClient := binance.NewFuturesClient(apiKey, secret)

// Définir le levier
_, err := futuresClient.NewChangeLeverageService().
    Symbol("BTCUSDT").Leverage(10).Do(ctx)

// Ouvrir une position longue
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)

// Consulter les positions
positions, _ := futuresClient.NewGetPositionRiskService().Do(ctx)
for _, p := range positions {
    if p.PositionAmt != "0" {
        fmt.Printf("%s Position %s @ %s\n", p.Symbol, p.PositionAmt, p.EntryPrice)
    }
}

IV. Abonnement 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("Échec de connexion :", err, ", nouvel essai dans 5s")
            time.Sleep(5 * time.Second)
            continue
        }
        log.Println("WS Connecté")

        // Goroutine pour le battement de cœur (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("Erreur de lecture :", 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. Passage d'ordres concurrents avec goroutines

Le modèle de concurrence de Go est particulièrement adapté pour passer plusieurs ordres simultanément :

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
}

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

Remarque : Le marché Spot de Binance limite à 100 ordres toutes les 10 secondes. Ne dépassez pas 50 goroutines simultanées pour éviter de déclencher la limitation de fréquence.

VI. Questions fréquemment posées (FAQ)

Q1 : Quelle est la taille du binaire Go compilé ? Y a-t-il des dépendances ?

R : Un programme doté des fonctionnalités complètes de l'API Binance + WebSocket pèse environ 8 à 15 Mo après compilation. Il s'exécute directement sur Linux ou Docker sans aucune dépendance d'exécution (contrairement à Node.js qui nécessite des centaines de Mo de node_modules).

Q2 : Quelle est la différence de performance entre go-binance et net/http natif ?

R : L'écart est minime car go-binance n'est qu'une fine couche d'encodage. Un appel REST unique prend 5 à 8 ms, et la latence p99 lors d'un test de charge de 1000 appels concurrents est inférieure à 30 ms. Comparativement aux 80-100 ms de Python, Go offre un avantage de plus de 10 fois.

Q3 : Comment gérer élégamment les erreurs renvoyées par Binance ?

R : Le corps de l'erreur renvoyé par Binance est {"code" : -1021, "msg" : "..."}. Il est recommandé de définir un type BinanceError pour une gestion unifiée :

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

Q4 : Le WebSocket Go doit-il gérer lui-même la déconnexion après 24 heures ?

R : Oui. Binance déconnecte de force tous les WS après 24 heures. gorilla/websocket n'a pas de mécanisme de reconnexion automatique. Utilisez le modèle de boucle for + Dial de la section IV pour une reconnexion fluide.

Q5 : Comment déployer dans Docker ?

R : Utilisez une construction en plusieurs étapes pour une image finale de seulement 20 Mo :

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"]

Après avoir consulté la solution en Go, revenez à la Navigation par catégorie pour découvrir d'autres tutoriels sur les SDK dans d'autres langages dans la catégorie « Intégration API ».

Continuer la navigation

Vous avez encore des questions sur l'utilisation de Binance ? Retournez à la page de catégorie pour trouver d'autres tutoriels sur le même sujet.

Navigation par catégorie

Tutoriels connexes

Comment demander une API Binance ? Comment générer généralement les signatures de clés 2026-04-14 Comment utiliser l'API Binance Spot ? Code prêt à l'emploi de zéro à votre premier ordre 2026-04-14 Quelles sont les différences entre l'API Binance Futures et l'API Spot ? Comparaison des endpoints, paramètres et poids 2026-04-14 L'API Binance peut-elle bloquer mon IP ? Explication détaillée de la stratégie de limitation de débit et du calcul du poids 2026-04-14