Código de ejemplo para la API REST y WebSocket de Go

Última actualización: 2 abr 2025

Introducción

A continuación se muestra un script de ejemplo en Go que puede consultarse para la implementación REST y WebSocket de la API de Kraken.

Demuestra la siguiente funcionalidad:

  • Puntos finales de la API REST pública

  • Puntos finales de la API REST privada

  • Suscripciones a la API WebSocket pública

  • Suscripciones a la API WebSocket privada

Instalación

Para ejecutarlo localmente, puede copiar/pegar el código siguiente.

Tenga en cuenta que al establecer la instrucción if de 1 == 0 en 1 == 1 se ejecutará el bloque de código específico.

Antes de ejecutar el script, debe actualizar el script con sus claves API públicas y privadas.

Puede descargar este archivo aquí.

Código Go

bash

Bash

package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "crypto/sha512"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "os/signal"
    "strings"
    "time"

    "github.com/sacOO7/gowebsocket"
)

// WsTokenJsonStruct represents the JSON structure of the WebSocket token response
type WsTokenJsonStruct struct {
    Error []interface{} `json:"error"`
    Result struct {
        Token string `json:"token"`
        Expires int `json:"expires"`
    } `json:"result"`
}

func main() {
    // TODO: Replace the Public and Private key with YOUR keys :)
    apiPublicKey := "YOUR_PUBLIC_KEY"
    apiPrivateKey := "YOUR_PRIVATE_KEY"

    fmt.Println("|==========================================|")
    fmt.Println("| KRAKEN.COM GOLANG TEST APP |")
    fmt.Println("|==========================================|")
    fmt.Println()

    // PUBLIC REST API Examples
    if 1 == 0 {
        publicEndpoint := "SystemStatus"
        publicInputParameters := ""

        // MORE PUBLIC REST EXAMPLES
        // publicEndpoint := "AssetPairs"
        // publicInputParameters := "pair=ethusd,xbtusd"
        // publicEndpoint := "Ticker"
        // publicInputParameters := "pair=ethusd"
        // publicEndpoint := "Trades"
        // publicInputParameters := "pair=ethusd&since=0"

        publicResponse := QueryPublicEndpoint(publicEndpoint, publicInputParameters)
        fmt.Println(publicResponse)
    }

    // PRIVATE REST API Examples
    if 0 == 0 {
        privateEndpoint := "Balance"
        privateInputParameters := ""

        // MORE PRIVATE REST EXAMPLES
        // privateEndpoint := "AddOrder"
        // privateInputParameters := "pair=xbteur&type=buy&ordertype=limit&price=1.00&volume=1"
        // privateEndpoint := "AddOrder"
        // privateInputParameters := "pair=xdgeur&type=sell&ordertype=limit&volume=3000&price=%2b10.0%" // Positive Percentage Example (%2 represents +, which is a reserved character in HTTP)
        // privateEndpoint := "AddOrder"
        // privateInputParameters := "pair=xdgeur&type=sell&ordertype=limit&volume=3000&price=-10.0%" // Negative Percentage Example
        // privateEndpoint := "AddOrder"
        // privateInputParameters := "pair=xdgeur&type=buy&ordertype=market&volume=3000&userref=789" // Userref Example
        // privateEndpoint := "Balance" // {"error":[]} IS SUCCESS, Means EMPTY BALANCE
        // privateInputParameters := ""
        // privateEndpoint := "QueryOrders"
        // privateInputParameters := "txid=OFUSL6-GXIIT-KZ2JDJ"
        // privateEndpoint := "AddOrder"
        // privateInputParameters := "pair=xdgusd&type=buy&ordertype=market&volume=5000"
        // privateEndpoint := "DepositAddresses"
        // privateInputParameters := "asset=xbt&method=Bitcoin"
        // privateEndpoint := "DepositMethods"
        // privateInputParameters := "asset=eth"
        // privateEndpoint := "WalvarTransfer"
        // privateInputParameters := "asset=xbt&to=Futures Walvar&from=Spot Walvar&amount=0.0045"
        // privateEndpoint := "TradesHistory"
        // privateInputParameters := "start=1577836800&end=1609459200"
        // privateEndpoint := "GetWebSocketsToken"
        // privateInputParameters := ""

        privateResponse := QueryPrivateEndpoint(privateEndpoint, privateInputParameters, apiPublicKey, apiPrivateKey)
        fmt.Println(privateResponse)
    }

    // PUBLIC WEBSOCKET Examples
    if 1 == 0 {
        publicWebSocketURL := "wss://ws.kraken.com/"
        publicWebSocketSubscriptionMsg := "{ \"event\":\"subscribe\", \"subscription\":{\"name\":\"trade\"},\"pair\":[\"XBT/USD\"] }"

        // MORE PUBLIC WEBSOCKET EXAMPLES
        // publicWebSocketSubscriptionMsg := "{ \"event\": \"subscribe\", \"subscription\": { \"interval\": 1440, \"name\": \"ohlc\"}, \"pair\": [ \"XBT/EUR\"]}"
        // publicWebSocketSubscriptionMsg := "{ \"event\": \"subscribe\", \"subscription\": { \"name\": \"spread\"}, \"pair\": [ \"XBT/EUR\",\"ETH/USD\" ]}"

        OpenAndStreamWebSocketSubscription(publicWebSocketURL, publicWebSocketSubscriptionMsg)
    }

    // PRIVATE WEBSOCKET Examples
    if 1 == 0 {
        privateWebSocketURL := "wss://ws-auth.kraken.com/"

        // GET THE WEBSOCKET TOKEN FORM THE JSON RESPONSE
        response := GetWebSocketToken("GetWebSocketsToken", "", apiPublicKey, apiPrivateKey)
        webSocketToken := response.Result.Token

        // MORE PRIVATE WEBSOCKET EXAMPLES
        // privateWebSocketSubscriptionMsg := "{\"event\": \"subscribe\", \"subscription\": { \"name\": \"openOrders\", \"token\": \"" + webSocketToken + "\"}}"
        // privateWebSocketSubscriptionMsg := "{\"event\": \"subscribe\", \"subscription\": { \"name\": \"balances\", \"token\": \"" + webSocketToken + "\"}}"
        // privateWebSocketSubscriptionMsg := "{\"event\":\"addOrder\",\"reqid\":1234,\"ordertype\":\"limit\",\"pair\":\"XBT/EUR\",\"token\": \"" + webSocketToken + "\",\"type\":\"buy\",\"volume\":\"1\", \"price\":\"1.00\"}"

        // REPLACE PLACEHOLDER WITH TOKEN
        privateWebSocketSubscriptionMsg := "{ \"event\": \"subscribe\", \"subscription\": { \"name\": \"ownTrades\", \"token\": \"" + webSocketToken + "\"}}"

        OpenAndStreamWebSocketSubscription(privateWebSocketURL, privateWebSocketSubscriptionMsg)
    }

    fmt.Println()
    fmt.Println("|=======================================|")
    fmt.Println("| END OF PROGRAM - HAVE A GOOD DAY :) |")
    fmt.Println("|=======================================|")
    fmt.Println("\n")
}

// QueryPublicEndpoint queries a public endpoint on the Kraken API
func QueryPublicEndpoint(endPointName, inputParameters string) string {
    baseDomain := "https://api.kraken.com"
    publicPath := "/0/public/"
    apiEndpointFullURL := baseDomain + publicPath + endPointName + "?" + inputParameters

    resp, err := http.Get(apiEndpointFullURL)
    if err != nil {
        fmt.Println("ERROR OCCURED: ", err)
        os.Exit(1)
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("ERROR OCCURED: ", err)
        os.Exit(1)
    }

    return string(body)
}

// QueryPrivateEndpoint queries a private endpoint on the Kraken API
func QueryPrivateEndpoint(endPointName, inputParameters, apiPublicKey, apiPrivateKey string) string {
    baseDomain := "https://api.kraken.com"
    privatePath := "/0/private/"
    apiEndpointFullURL := baseDomain + privatePath + endPointName + "?" + inputParameters

    nonce := fmt.Sprintf("%d", time.Now().Unix())
    apiPostBodyData := "nonce=" + nonce + "&" + inputParameters
    signature := CreateAuthenticationSignature(apiPrivateKey, privatePath, endPointName, nonce, apiPostBodyData)

    httpOptions, err := http.NewRequest("POST", apiEndpointFullURL, strings.NewReader(apiPostBodyData))
    httpOptions.Header.Add("API-Key", apiPublicKey)
    httpOptions.Header.Add("API-Sign", signature)
    httpOptions.Header.Add("User-Agent", "GO Lang Client")

    if err != nil {
        fmt.Println("ERROR OCCURED: ", err)
        os.Exit(1)
    }

    resp, err := http.DefaultClient.Do(httpOptions)
    if err != nil {
        fmt.Println("ERROR OCCURED: ", err)
        os.Exit(1)
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("ERROR OCCURED: ", err)
        os.Exit(1)
    }

    jsonData := string(body)
    return jsonData
}

// GetWebSocketToken gets a WebSocket token from the Kraken API
func GetWebSocketToken(endPointName, inputParameters, apiPublicKey, apiPrivateKey string) WsTokenJsonStruct {
    baseDomain := "https://api.kraken.com"
    privatePath := "/0/private/"
    apiEndpointFullURL := baseDomain + privatePath + endPointName + "?" + inputParameters

    nonce := fmt.Sprintf("%d", time.Now().Unix())
    apiPostBodyData := "nonce=" + nonce + "&" + inputParameters
    signature := CreateAuthenticationSignature(apiPrivateKey, privatePath, endPointName, nonce, apiPostBodyData)

    httpOptions, err := http.NewRequest("POST", apiEndpointFullURL, strings.NewReader(apiPostBodyData))
    httpOptions.Header.Add("API-Key", apiPublicKey)
    httpOptions.Header.Add("API-Sign", signature)
    httpOptions.Header.Add("User-Agent", "GO Lang Client")

    if err != nil {
        fmt.Println("ERROR OCCURED: ", err)
        os.Exit(1)
    }

    resp, err := http.DefaultClient.Do(httpOptions)
    if err != nil {
        fmt.Println("ERROR OCCURED: ", err)
        os.Exit(1)
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("ERROR OCCURED: ", err)
        os.Exit(1)
    }

    var jsonData WsTokenJsonStruct
    json.Unmarshal(body, &jsonData)
    return jsonData
}

// CreateAuthenticationSignature creates an authentication signature for the Kraken API
func CreateAuthenticationSignature(apiPrivateKey, apiPath, endPointName, nonce, apiPostBodyData string) string {
    apiPost := nonce + apiPostBodyData
    secret, _ := base64.StdEncoding.DecodeString(apiPrivateKey)
    apiEndpointPath := apiPath + endPointName
    sha := sha256.New()
    sha.Write([]byte(apiPost))
    shaSum := sha.Sum(nil)
    allBytes := append([]byte(apiEndpointPath), shaSum...)
    mac := hmac.New(sha512.New, secret)
    mac.Write(allBytes)
    macSum := mac.Sum(nil)
    signatureString := base64.StdEncoding.EncodeToString(macSum)
    return signatureString
}

// OpenAndStreamWebSocketSubscription opens and streams a WebSocket subscription
func OpenAndStreamWebSocketSubscription(connectionURL, webSocketSubscription string) {
    interrupt := make(chan os.Signal, 1)
    signal.Notify(interrupt, os.Interrupt)

    webSocketClient := gowebsocket.New(connectionURL)

    webSocketClient.OnConnectError = func(err error, socket gowebsocket.Socket) {
        fmt.Println("Received connect error - ", err)
    }

    webSocketClient.OnConnected = func(socket gowebsocket.Socket) {
        fmt.Println("Connected to server")
    }

    webSocketClient.OnTextMessage = func(message string, socket gowebsocket.Socket) {
        fmt.Println(time.Now().Format("01-02-2006 15:04:05") + ": " + message)
    }

    webSocketClient.OnPingReceived = func(message string, socket gowebsocket.Socket) {
        fmt.Println("Received ping - " + message)
    }

    webSocketClient.OnPongReceived = func(message string, socket gowebsocket.Socket) {
        fmt.Println("Received pong - " + message)
    }

    webSocketClient.OnDisconnected = func(err error, socket gowebsocket.Socket) {
        fmt.Println("Socket Closed")
        fmt.Println("\n")
    }

    webSocketClient.Connect()
    webSocketClient.SendText(webSocketSubscription)

    for {
        select {
        case <-interrupt:
            log.Println("interrupt")
            webSocketClient.Close()
            return
        }
    }
}

¿Necesita más ayuda?