mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-15 15:09:55 +00:00
Updated support for Bitfinex Websocket API.
This commit is contained in:
@@ -179,6 +179,7 @@ type Bitfinex struct {
|
||||
AvailablePairs []string
|
||||
EnabledPairs []string
|
||||
WebsocketConn *websocket.Conn
|
||||
WebsocketSubdChannels map[int]BitfinexWebsocketChanInfo
|
||||
}
|
||||
|
||||
func (b *Bitfinex) SetDefaults() {
|
||||
@@ -187,6 +188,7 @@ func (b *Bitfinex) SetDefaults() {
|
||||
b.Verbose = false
|
||||
b.Websocket = false
|
||||
b.RESTPollingDelay = 10
|
||||
b.WebsocketSubdChannels = make(map[int]BitfinexWebsocketChanInfo)
|
||||
}
|
||||
|
||||
func (b *Bitfinex) GetName() string {
|
||||
|
||||
@@ -4,61 +4,106 @@ import (
|
||||
"github.com/gorilla/websocket"
|
||||
"log"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
/* 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"
|
||||
BITFINEX_WEBSOCKET = "wss://api2.bitfinex.com:3000/ws"
|
||||
BITFINEX_WEBSOCKET_VERSION = "1.0"
|
||||
)
|
||||
|
||||
type BitfinexWebsocketFrameRequest struct {
|
||||
Type string `json:"type"`
|
||||
M int `json:"m"`
|
||||
I int `json:"i"`
|
||||
Subscription string `json:"n"`
|
||||
Params string `json:"o"`
|
||||
type BitfinexWebsocketChanInfo struct {
|
||||
Channel string
|
||||
Pair string
|
||||
}
|
||||
|
||||
type BitfinexWebsocketResponse struct {
|
||||
M int `json:"m"`
|
||||
I int `json:"i"`
|
||||
Subscription string `json:"n"`
|
||||
Params string `json:"o"`
|
||||
type BitfinexWebsocketBook struct {
|
||||
Price float64
|
||||
Count int
|
||||
Amount float64
|
||||
}
|
||||
|
||||
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
|
||||
type BitfinexWebsocketTrade struct {
|
||||
ID int64
|
||||
Timestamp int64
|
||||
Price float64
|
||||
Amount float64
|
||||
}
|
||||
|
||||
paramsEncoded, err := JSONEncode(params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msg.Params = string(paramsEncoded)
|
||||
type BitfinexWebsocketTicker struct {
|
||||
Bid float64
|
||||
BidSize float64
|
||||
Ask float64
|
||||
AskSize float64
|
||||
DailyChange float64
|
||||
DialyChangePerc float64
|
||||
LastPrice float64
|
||||
Volume float64
|
||||
}
|
||||
|
||||
msgEncoded, err := JSONEncode(msg)
|
||||
func (b *Bitfinex) WebsocketPingHandler() error {
|
||||
request := make(map[string]string)
|
||||
request["event"] = "ping"
|
||||
return b.WebsocketSend(request)
|
||||
}
|
||||
|
||||
func (b *Bitfinex) WebsocketSend(data interface{}) error {
|
||||
json, err := JSONEncode(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = b.WebsocketConn.WriteMessage(websocket.TextMessage, msgEncoded)
|
||||
err = b.WebsocketConn.WriteMessage(websocket.TextMessage, json)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*counter += 2
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bitfinex) WebsocketSubscribe(channel string, params map[string]string) {
|
||||
request := make(map[string]string)
|
||||
request["event"] = "subscribe"
|
||||
request["channel"] = channel
|
||||
|
||||
if len(params) > 0 {
|
||||
for k, v := range params {
|
||||
request[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
b.WebsocketSend(request)
|
||||
}
|
||||
|
||||
func (b *Bitfinex) WebsocketSendAuth() error {
|
||||
request := make(map[string]interface{})
|
||||
payload := "AUTH" + strconv.FormatInt(time.Now().UnixNano(), 10)[:13]
|
||||
request["event"] = "auth"
|
||||
request["apiKey"] = b.APIKey
|
||||
request["authSig"] = HexEncodeToString(GetHMAC(HASH_SHA512_384, []byte(payload), []byte(b.APISecret)))
|
||||
request["authPayload"] = payload
|
||||
return b.WebsocketSend(request)
|
||||
}
|
||||
|
||||
func (b *Bitfinex) WebsocketSendUnauth() error {
|
||||
request := make(map[string]string)
|
||||
request["event"] = "unauth"
|
||||
return b.WebsocketSend(request)
|
||||
}
|
||||
|
||||
func (b *Bitfinex) WebsocketAddSubscriptionChannel(chanID int, channel, pair string) {
|
||||
chanInfo := BitfinexWebsocketChanInfo{Pair: pair, Channel: channel}
|
||||
b.WebsocketSubdChannels[chanID] = chanInfo
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("%s Subscribed to Channel: %s Pair: %s ChannelID: %d\n", b.GetName(), channel, pair, chanID)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bitfinex) WebsocketClient() {
|
||||
msgCounter := 0
|
||||
channels := []string{"book", "trades", "ticker"}
|
||||
for b.Enabled && b.Websocket {
|
||||
var Dialer websocket.Dialer
|
||||
var err error
|
||||
@@ -69,24 +114,46 @@ func (b *Bitfinex) WebsocketClient() {
|
||||
continue
|
||||
}
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("%s Connected to Websocket.\n", b.GetName())
|
||||
msgType, resp, err := b.WebsocketConn.ReadMessage()
|
||||
if msgType != websocket.TextMessage {
|
||||
continue
|
||||
}
|
||||
|
||||
err = b.Subscribe(&msgCounter, BITFINEX_WEBSOCKET_ORDERBOOK, map[string]interface{}{"ExchangeID": "0", "ProductPairID": "1", "Depth": 25, "RoundToDecimals": 2})
|
||||
type WebsocketHandshake struct {
|
||||
Event string `json:"event"`
|
||||
Code int64 `json:"code"`
|
||||
Version int `json:"version"`
|
||||
}
|
||||
|
||||
hs := WebsocketHandshake{}
|
||||
err = JSONDecode(resp, &hs)
|
||||
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 hs.Event == "info" {
|
||||
if b.Verbose {
|
||||
log.Printf("%s Connected to Websocket.\n", b.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("%s Subscribed to channels.\n", b.GetName())
|
||||
for _, x := range channels {
|
||||
for _, y := range b.EnabledPairs {
|
||||
params := make(map[string]string)
|
||||
if x == "book" {
|
||||
params["prec"] = "P0"
|
||||
}
|
||||
params["pair"] = y
|
||||
b.WebsocketSubscribe(x, params)
|
||||
}
|
||||
}
|
||||
|
||||
if b.AuthenticatedAPISupport {
|
||||
err = b.WebsocketSendAuth()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
for b.Enabled && b.Websocket {
|
||||
@@ -98,14 +165,79 @@ func (b *Bitfinex) WebsocketClient() {
|
||||
|
||||
switch msgType {
|
||||
case websocket.TextMessage:
|
||||
msg := BitfinexWebsocketResponse{}
|
||||
err := JSONDecode(resp, &msg)
|
||||
var result interface{}
|
||||
err := JSONDecode(resp, &result)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
log.Println(string(resp))
|
||||
log.Println(msg)
|
||||
|
||||
switch reflect.TypeOf(result).String() {
|
||||
case "map[string]interface {}":
|
||||
eventData := result.(map[string]interface{})
|
||||
event := eventData["event"]
|
||||
|
||||
switch event {
|
||||
case "subscribed":
|
||||
b.WebsocketAddSubscriptionChannel(int(eventData["chanId"].(float64)), eventData["channel"].(string), eventData["pair"].(string))
|
||||
case "auth":
|
||||
status := eventData["status"].(string)
|
||||
|
||||
if status == "OK" {
|
||||
b.WebsocketAddSubscriptionChannel(0, "account", "N/A")
|
||||
} else if status == "fail" {
|
||||
log.Printf("%s Websocket unable to AUTH. Error code: %s\n", b.GetName(), eventData["code"].(string))
|
||||
b.AuthenticatedAPISupport = false
|
||||
}
|
||||
}
|
||||
case "[]interface {}":
|
||||
chanData := result.([]interface{})
|
||||
chanID := int(chanData[0].(float64))
|
||||
chanInfo, ok := b.WebsocketSubdChannels[chanID]
|
||||
|
||||
if !ok {
|
||||
log.Println("Unable to locate chanID: %d", chanID)
|
||||
} else {
|
||||
switch chanInfo.Channel {
|
||||
case "book":
|
||||
orderbook := []BitfinexWebsocketBook{}
|
||||
switch len(chanData) {
|
||||
case 2:
|
||||
data := chanData[1].([]interface{})
|
||||
for _, x := range data {
|
||||
y := x.([]interface{})
|
||||
orderbook = append(orderbook, BitfinexWebsocketBook{Price: y[0].(float64), Count: int(y[1].(float64)), Amount: y[2].(float64)})
|
||||
}
|
||||
case 4:
|
||||
orderbook = append(orderbook, BitfinexWebsocketBook{Price: chanData[1].(float64), Count: int(chanData[2].(float64)), Amount: chanData[3].(float64)})
|
||||
}
|
||||
case "ticker":
|
||||
ticker := BitfinexWebsocketTicker{Bid: chanData[1].(float64), BidSize: chanData[2].(float64), Ask: chanData[3].(float64), AskSize: chanData[4].(float64),
|
||||
DailyChange: chanData[5].(float64), DialyChangePerc: chanData[6].(float64), LastPrice: chanData[7].(float64), Volume: chanData[8].(float64)}
|
||||
|
||||
log.Printf("Bitfinex %s Websocket Last %f Volume %f\n", chanInfo.Pair, ticker.LastPrice, ticker.Volume)
|
||||
case "account":
|
||||
//to-do
|
||||
case "trades":
|
||||
trades := []BitfinexWebsocketTrade{}
|
||||
switch len(chanData) {
|
||||
case 2:
|
||||
data := chanData[1].([]interface{})
|
||||
for _, x := range data {
|
||||
y := x.([]interface{})
|
||||
trades = append(trades, BitfinexWebsocketTrade{ID: int64(y[0].(float64)), Timestamp: int64(y[1].(float64)), Price: y[2].(float64), Amount: y[3].(float64)})
|
||||
}
|
||||
case 5:
|
||||
trade := BitfinexWebsocketTrade{ID: int64(chanData[1].(float64)), Timestamp: int64(chanData[2].(float64)), Price: chanData[3].(float64), Amount: chanData[4].(float64)}
|
||||
trades = append(trades, trade)
|
||||
|
||||
if b.Verbose {
|
||||
log.Printf("Bitfinex %s Websocket Trade ID %d Timestamp %d Price %f Amount %f\n", chanInfo.Pair, trade.ID, trade.Timestamp, trade.Price, trade.Amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
b.WebsocketConn.Close()
|
||||
|
||||
Reference in New Issue
Block a user