Mã ví dụ cho API Go REST và WebSocket

Cập nhật lần cuối: 2 thg 4, 2025

Giới thiệu

Dưới đây là một tập lệnh ví dụ Go có thể được tham chiếu cho cả triển khai RESTWebSocket của API Kraken.

Nó trình bày các chức năng sau:

  • Điểm cuối API REST công khai

  • Điểm cuối API REST riêng tư

  • Đăng ký API WebSocket công khai

  • Đăng ký API WebSocket riêng tư

Cài đặt

Để chạy cục bộ, bạn có thể sao chép/dán mã bên dưới.

Lưu ý rằng việc đặt câu lệnh if của 1 == 0 thành 1 == 1 sẽ thực thi khối mã cụ thể.

Trước khi chạy tập lệnh, bạn cần cập nhật tập lệnh bằng Khóa API công khai và riêng tư của bạn.

Bạn có thể tải xuống tệp này tại đây.

Mã 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
        }
    }
}

Bạn cần thêm trợ giúp?