aopoltorzhicky / go_kraken Goto Github PK
View Code? Open in Web Editor NEWGo library for Kraken Websocket and REST API
License: MIT License
Go library for Kraken Websocket and REST API
License: MIT License
Hi. Thanks so much for this library.
I'm trying my best to implement a simple local orderbook, as all the examples suggest. However, after a few seconds, I keep hitting a checksum mismatch. If I increase the depth to 100, it lasts a whole lot longer but still crashes after about 30 minutes or so.
My only thought is processing speed but I'm on a 2.9GHz Quadcore i7 Macbook Pro, I don't think I should be having processing speed issues? Activity Monitor shows nothing hectic either.
Here's my code if anyone feels like lending an eye.
package kraken
import (
"errors"
"fmt"
"hash/crc32"
"os"
"os/signal"
e "router/exchanges"
log "router/logging"
"sort"
"strings"
"syscall"
ws "github.com/aopoltorzhicky/go_kraken/websocket"
"github.com/shopspring/decimal"
)
var orderbook_btc_usd = e.IndexedBook{
Bids: make(map[float64]e.SubBook),
Asks: make(map[float64]e.SubBook),
}
func ConnectKrakenWS() {
signals := make(chan os.Signal, 1)
signal.Notify(signals, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
kraken := ws.NewKraken(ws.ProdBaseURL)
if err := kraken.Connect(); err != nil {
log.LogFatal(errors.New("Error connecting to web socket: " + err.Error()))
}
// subscribe to BTCUSD`s ticker
if err := kraken.SubscribeBook([]string{ws.BTCUSD}, ws.Depth10); err != nil {
log.LogFatal(errors.New("SubscribeTicker error: " + err.Error()))
}
orderbook_btc_usd = e.IndexedBook{
Bids: make(map[float64]e.SubBook),
Asks: make(map[float64]e.SubBook),
}
var processedCounter = 0
var recievedCounter = 0
// busy := false
for {
select {
case <-signals:
log.LogInfo("Stopping kraken WS...")
if err := kraken.Close(); err != nil {
log.LogFatal(err)
}
return
case update := <-kraken.Listen():
switch data := update.Data.(type) {
case ws.OrderBookUpdate:
recievedCounter += 1
// if !busy {
// busy = true
switch update.Pair {
case "XBT/USD":
handleBTCUSDUpdateEvent(&data)
}
processedCounter += 1
// busy = false
// }
default:
}
}
}
}
func handleBTCUSDUpdateEvent(v *ws.OrderBookUpdate) {
capture(orderbook_btc_usd.Bids, v.Bids)
capture(orderbook_btc_usd.Asks, v.Asks)
newBook := sortorderbook()
newBook.Pair.Currency1 = e.BTC
newBook.Pair.Currency2 = e.USD
if !v.IsSnapshot {
if !isBookValid(newBook, v.CheckSum) {
fmt.Println("Checksum failure. Expected", v.CheckSum)
// log.LogFatal(errors.New("Kraken checksum failure"))
return
}
}
e.Kraken.Book = newBook
e.Kraken.Active = true
}
func capture(liveSideCollection map[float64]e.SubBook, deltaCollection []ws.OrderBookItem) {
// for k, v := range liveSideCollection {
for i := range deltaCollection {
price, _ := deltaCollection[i].Price.Float64()
qty, _ := deltaCollection[i].Volume.Float64()
if qty == 0 {
delete(liveSideCollection, price)
} else {
liveSideCollection[price] = e.SubBook{
Price: price,
Qty: qty,
}
}
}
// }
}
func sortorderbook() (Book e.Book) {
c := orderbook_btc_usd
Book.Bids = []e.SubBook{}
for i := range c.Bids {
Book.Bids = append(Book.Bids, e.SubBook{
Price: c.Bids[i].Price,
Qty: c.Bids[i].Qty,
})
}
sort.Sort(e.BidSubBook(Book.Bids))
Book.Bids[0].Total = Book.Bids[0].Qty
for i := 1; i < 10; i++ {
Book.Bids[i].Total = Book.Bids[i-1].Total + Book.Bids[i].Qty
}
Book.Asks = []e.SubBook{}
for i := range c.Asks {
Book.Asks = append(Book.Asks, e.SubBook{
Price: c.Asks[i].Price,
Qty: c.Asks[i].Qty,
})
}
sort.Sort(e.AskSubBook(Book.Asks))
Book.Asks[0].Total = Book.Asks[0].Qty
for i := 1; i < 10; i++ {
Book.Asks[i].Total = Book.Asks[i-1].Total + Book.Asks[i].Qty
}
// Book.Bids = Book.Bids[:10]
// Book.Asks = Book.Asks[:10]
return Book
}
func isBookValid(newBook e.Book, challengeChecksum string) bool {
var str strings.Builder
// Literally here so I can print the current local orderbook for checksum comparisons
dict := map[string][][]string{}
for _, level := range newBook.Asks[:10] {
price := decimal.NewFromFloat(level.Price).StringFixed(5)
price = strings.Replace(price, ".", "", 1)
price = strings.TrimLeft(price, "0")
str.WriteString(price)
volume := decimal.NewFromFloat(level.Qty).StringFixed(8)
volume = strings.Replace(volume, ".", "", 1)
volume = strings.TrimLeft(volume, "0")
str.WriteString(volume)
dict["a"] = append(dict["a"], []string{
decimal.NewFromFloat(level.Price).StringFixed(5), decimal.NewFromFloat(level.Qty).StringFixed(8), "",
})
}
for _, level := range newBook.Bids[:10] {
price := decimal.NewFromFloat(level.Price).StringFixed(5)
price = strings.Replace(price, ".", "", 1)
price = strings.TrimLeft(price, "0")
str.WriteString(price)
volume := decimal.NewFromFloat(level.Qty).StringFixed(8)
volume = strings.Replace(volume, ".", "", 1)
volume = strings.TrimLeft(volume, "0")
str.WriteString(volume)
dict["b"] = append(dict["b"], []string{
decimal.NewFromFloat(level.Price).StringFixed(5), decimal.NewFromFloat(level.Qty).StringFixed(8), "",
})
}
checksum := str.String()
checksumInt := crc32.ChecksumIEEE([]byte(checksum))
checksumPass := fmt.Sprint(checksumInt) == challengeChecksum
if !checksumPass {
fmt.Println("Kraken checksum fail")
}
return checksumPass
}
Maybe I'm missing something, but the structs mentioned the title are both missing a ReqID field, which is present in Kraken API documentation. Without them I don't know how can I know which order an AddOrderResponse
belongs to. Is there any reason for this omission that I should be aware of?
Hello! Thank you so much for this library. It is super helpful.
Would you be open to accepting a pull request that expands the AddOrderRequest
object? I was hoping to utilize some additional fields that exist on the message.. (https://docs.kraken.com/websockets/#message-addOrder)
Fields I intend to add..
Thanks for this - looks very smooth. Heads up - I noticed that running the basic orderbook example gives an 'Unknown message type:' error inside client.handleChannel. Maybe I'm missing something?
I'm seeing the following 'panic: interface conversion: interface {} is string, not []interface {}' immediately after the first orderbook update (the snapshot is parsed fine). Anyone seen this before?
I noticed that my private feed was not able to re-subscribe after error. I noticed this error:
"Unsupported field: 'pair' for the given msg type: private subscribe: "
I think this solution might work...
func (k *Kraken) resubscribe() error {
for _, sub := range k.subscriptions {
switch sub.Subscription.Name {
// Private Channels
case ChanOwnTrades, ChanOpenOrders:
return k.subscribeToPrivate(sub.Subscription.Name)
default:
//All other channels can do normal auth
if err := k.send(SubscriptionRequest{
Event: EventSubscribe,
Pairs: []string{sub.Pair},
Subscription: sub.Subscription,
}); err != nil {
return err
}
}
}
return nil
}
Thank you very much for your work on this library.
I'm currently getting a "Can't parse data" error when receiving incoming websocket messages regarding open orders (listening for DataUpdates
from AuthClient.SubscribeOpenOrders
).
I believe the type assertion data.([]interface{})
is failing. I'm not sure if Kraken changed the message format recently?
Hi,
I'm having trouble with subscribing to open orders using the websocket module.
I copied the code from examples/auth/main.go in this repo and added my own keys.
When running it from the shell I got some output telling me connecting and subscribing to my own open orders succeeded.
2020/12/12 21:49:10 [WARN]: invalid data length: []interface {}{[]interface {}{
*** snip many go-objects with info of requests from the past ***
}}, "openOrders", map[string]interface {}{"sequence":1}}
As expected with these kind of errors, I don't receive any mesage on the channel returned by Listen().
It seems the data length is 3 and not 4 as the library expects.
Can you reproduce this on your end? Should I make a PR with a fix?
Regards,
lk16
Hello.
Your release v0.10.0 is 1 commit behind from master, so when people use go.mod, he get broken version without fix
Hi.
Issue with send on closed channel.
Example
goroutine 72 [running]:
github.com/aopoltorzhicky/go_kraken/websocket.(*Client).close(0xc001112500, 0x0, 0x0)
/home/dvp/go/pkg/mod/github.com/aopoltorzhicky/go_kraken/[email protected]/client.go:285 +0xc8
github.com/aopoltorzhicky/go_kraken/websocket.(*Client).exit(0xc001112500, 0x0, 0x0, 0x0, 0x0)
/home/dvp/go/pkg/mod/github.com/aopoltorzhicky/go_kraken/[email protected]/client.go:258 +0x52
github.com/aopoltorzhicky/go_kraken/websocket.(*Client).reconnect(0xc001112500, 0x0, 0x0, 0x0, 0x0)
/home/dvp/go/pkg/mod/github.com/aopoltorzhicky/go_kraken/[email protected]/client.go:222 +0x7a
github.com/aopoltorzhicky/go_kraken/websocket.(*Client).listenDisconnect(0xc001112500)
/home/dvp/go/pkg/mod/github.com/aopoltorzhicky/go_kraken/[email protected]/client.go:149 +0x357
created by github.com/aopoltorzhicky/go_kraken/websocket.(*Client).reset
/home/dvp/go/pkg/mod/github.com/aopoltorzhicky/go_kraken/[email protected]/client.go:189 +0x67
panic: send on closed channel
goroutine 30 [running]:
github.com/aopoltorzhicky/go_kraken/websocket.(*Client).close(0xc001112500, 0xdc0ca0, 0xc00018e230)
/home/dvp/go/pkg/mod/github.com/aopoltorzhicky/go_kraken/[email protected]/client.go:283 +0xad
github.com/aopoltorzhicky/go_kraken/websocket.(*Client).exit(0xc001112500, 0xdc0ca0, 0xc00018e230, 0x0, 0x0)
/home/dvp/go/pkg/mod/github.com/aopoltorzhicky/go_kraken/[email protected]/client.go:258 +0x52
github.com/aopoltorzhicky/go_kraken/websocket.(*Client).reconnect(0xc001112500, 0xdc0ca0, 0xc00018e230, 0x0, 0x0)
/home/dvp/go/pkg/mod/github.com/aopoltorzhicky/go_kraken/[email protected]/client.go:245 +0x5d0
github.com/aopoltorzhicky/go_kraken/websocket.(*Client).listenDisconnect(0xc001112500)
/home/dvp/go/pkg/mod/github.com/aopoltorzhicky/go_kraken/[email protected]/client.go:149 +0x357
created by github.com/aopoltorzhicky/go_kraken/websocket.(*Client).reset
/home/dvp/go/pkg/mod/github.com/aopoltorzhicky/go_kraken/[email protected]/client.go:189 +0x67
Just heard back from Kraken support and it appears the website was under maintenance
The error happened at 22:00 UTC.
https://status.kraken.com/incidents/x8xyn5rp29jf
Originally posted by @payaaam in #33 (comment)
Hi,
Is there a method for pulling WithDrawInfo ? In other words, testing if a key/address is whitelisted.
https://docs.kraken.com/rest/#tag/Funding/operation/getWithdrawalInformation
Thank you
The Kraken API has an array where you have map the fields. If this was json, this wouldn't have happened. Anyways, I think we mixed up the bid / ask in the unmarshal function.
https://docs.kraken.com/websockets/#message-spread
PR to fix: #34
Line 35 in 3d9889a
Hi!
I received this error and the client was not able to recover? I sent out an email to Kraken support to understand this error code a bit more. Is the correct way to handle this documented somewhere? Happy to make the change myself, just trying to gather information.
websocket: close 1013: Market data unavailable
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.