From 793ea76d43072319a0989f09a2bbfee8a80e3330 Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Sat, 30 May 2015 19:06:49 +1000 Subject: [PATCH] Added basic Bitfinex Websocket implementation. --- README.md | 2 +- bitfinexhttp.go | 7 +++ bitfinexwebsocket.go | 114 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 bitfinexwebsocket.go diff --git a/README.md b/README.md index 1762ca1b..f4e5e379 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A cryptocurrency trading bot supporting multiple exchanges written in Golang. |----------|------|-----------|-----| | Alphapoint | Yes | Yes | NA | | ANXPRO | Yes | No | NA | -| Bitfinex | Yes | NA | NA | +| Bitfinex | Yes | Yes | NA | | Bitstamp | Yes | Yes | NA | | BTCChina | Yes | Yes | No | | BTCE | Yes | NA | NA | diff --git a/bitfinexhttp.go b/bitfinexhttp.go index 16eb6e47..131c6cb3 100644 --- a/bitfinexhttp.go +++ b/bitfinexhttp.go @@ -3,6 +3,7 @@ package main import ( "errors" "fmt" + "github.com/gorilla/websocket" "log" "strconv" "strings" @@ -159,6 +160,7 @@ type Bitfinex struct { BaseCurrencies []string AvailablePairs []string EnabledPairs []string + WebsocketConn *websocket.Conn } func (b *Bitfinex) SetDefaults() { @@ -188,10 +190,15 @@ func (b *Bitfinex) SetAPIKeys(apiKey, apiSecret string) { func (b *Bitfinex) Run() { if b.Verbose { + log.Printf("%s Websocket: %s.", b.GetName(), IsEnabled(b.Websocket)) log.Printf("%s polling delay: %ds.\n", b.GetName(), b.RESTPollingDelay) log.Printf("%s %d currencies enabled: %s.\n", b.GetName(), len(b.EnabledPairs), b.EnabledPairs) } + if b.Websocket { + go b.WebsocketClient() + } + exchangeProducts, err := b.GetSymbols() if err != nil { log.Printf("%s Failed to get available symbols.\n", b.GetName()) diff --git a/bitfinexwebsocket.go b/bitfinexwebsocket.go new file mode 100644 index 00000000..dc647015 --- /dev/null +++ b/bitfinexwebsocket.go @@ -0,0 +1,114 @@ +package main + +import ( + "github.com/gorilla/websocket" + "log" + "net/http" +) + +/* Implementation based off Bitfinex's bfws.bitfinex.com websocket service */ + +const ( + BITFINEX_WEBSOCKET = "ws://websocket.bitfinex.com:8086/WSGateway/" + BTIFINEX_WEBSOCKET_TRADES = "SubscribeTrades" + BITFINEX_WEBSOCKET_ORDERBOOK = "SubscribeLevel2" +) + +type BitfinexWebsocketFrameRequest struct { + Type string `json:"type"` + M int `json:"m"` + I int `json:"i"` + Subscription string `json:"n"` + Params string `json:"o"` +} + +type BitfinexWebsocketResponse struct { + M int `json:"m"` + I int `json:"i"` + Subscription string `json:"n"` + Params string `json:"o"` +} + +func (b *Bitfinex) Subscribe(counter *int, subscription string, params map[string]interface{}) error { + msg := BitfinexWebsocketFrameRequest{} + msg.Type = "anws_frame" + msg.M = 0 + msg.I = *counter + msg.Subscription = subscription + + paramsEncoded, err := JSONEncode(params) + if err != nil { + return err + } + msg.Params = string(paramsEncoded) + + msgEncoded, err := JSONEncode(msg) + if err != nil { + return err + } + + err = b.WebsocketConn.WriteMessage(websocket.TextMessage, msgEncoded) + + if err != nil { + return err + } + + *counter += 2 + return nil +} + +func (b *Bitfinex) WebsocketClient() { + msgCounter := 0 + for b.Enabled && b.Websocket { + var Dialer websocket.Dialer + var err error + b.WebsocketConn, _, err = Dialer.Dial(BITFINEX_WEBSOCKET, http.Header{}) + + if err != nil { + log.Printf("%s Unable to connect to Websocket. Error: %s\n", b.GetName(), err) + continue + } + + if b.Verbose { + log.Printf("%s Connected to Websocket.\n", b.GetName()) + } + + err = b.Subscribe(&msgCounter, BITFINEX_WEBSOCKET_ORDERBOOK, map[string]interface{}{"ExchangeID": "0", "ProductPairID": "1", "Depth": 25, "RoundToDecimals": 2}) + if err != nil { + log.Println(err) + continue + } + + err = b.Subscribe(&msgCounter, BTIFINEX_WEBSOCKET_TRADES, map[string]interface{}{"ExchangeID": "0", "ProductPairID": "1", "IncludeLastCount": "100"}) + if err != nil { + log.Println(err) + continue + } + + if b.Verbose { + log.Printf("%s Subscribed to channels.\n", b.GetName()) + } + + for b.Enabled && b.Websocket { + msgType, resp, err := b.WebsocketConn.ReadMessage() + if err != nil { + log.Println(err) + break + } + + switch msgType { + case websocket.TextMessage: + msg := BitfinexWebsocketResponse{} + err := JSONDecode(resp, &msg) + if err != nil { + log.Println(err) + continue + } + log.Println(string(resp)) + log.Println(msg) + } + } + b.WebsocketConn.Close() + log.Printf("%s Websocket client disconnected.\n", b.GetName()) + } +}