mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-02 23:16:51 +00:00
254 lines
7.4 KiB
Go
254 lines
7.4 KiB
Go
package lakebtc
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/thrasher-corp/gocryptotrader/common"
|
|
"github.com/thrasher-corp/gocryptotrader/currency"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
|
|
log "github.com/thrasher-corp/gocryptotrader/logger"
|
|
"github.com/toorop/go-pusher"
|
|
)
|
|
|
|
const (
|
|
lakeBTCWSURL = "ws.lakebtc.com:8085"
|
|
marketGlobalEndpoint = "market-global"
|
|
marketSubstring = "market-"
|
|
globalSubstring = "-global"
|
|
tickerBuyString = "buy"
|
|
tickerHighString = "high"
|
|
tickerLastString = "last"
|
|
tickerLowString = "low"
|
|
tickerSellString = "sell"
|
|
tickerVolumeString = "volume"
|
|
wssSchem = "wss"
|
|
)
|
|
|
|
// WsConnect initiates a new websocket connection
|
|
func (l *LakeBTC) WsConnect() error {
|
|
if !l.Websocket.IsEnabled() || !l.IsEnabled() {
|
|
return errors.New(wshandler.WebsocketNotEnabled)
|
|
}
|
|
var err error
|
|
l.WebsocketConn.Client, err = pusher.NewCustomClient(strings.ToLower(l.Name), lakeBTCWSURL, wssSchem)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = l.WebsocketConn.Client.Subscribe(marketGlobalEndpoint)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
l.GenerateDefaultSubscriptions()
|
|
err = l.listenToEndpoints()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go l.wsHandleIncomingData()
|
|
return nil
|
|
}
|
|
|
|
func (l *LakeBTC) listenToEndpoints() error {
|
|
var err error
|
|
l.WebsocketConn.Ticker, err = l.WebsocketConn.Client.Bind("tickers")
|
|
if err != nil {
|
|
return fmt.Errorf("%s Websocket Bind error: %s", l.GetName(), err)
|
|
}
|
|
l.WebsocketConn.Orderbook, err = l.WebsocketConn.Client.Bind("update")
|
|
if err != nil {
|
|
return fmt.Errorf("%s Websocket Bind error: %s", l.GetName(), err)
|
|
}
|
|
l.WebsocketConn.Trade, err = l.WebsocketConn.Client.Bind("trades")
|
|
if err != nil {
|
|
return fmt.Errorf("%s Websocket Bind error: %s", l.GetName(), err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions()
|
|
func (l *LakeBTC) GenerateDefaultSubscriptions() {
|
|
var subscriptions []wshandler.WebsocketChannelSubscription
|
|
enabledCurrencies := l.GetEnabledPairs(asset.Spot)
|
|
for j := range enabledCurrencies {
|
|
enabledCurrencies[j].Delimiter = ""
|
|
channel := fmt.Sprintf("%v%v%v", marketSubstring, enabledCurrencies[j].Lower(), globalSubstring)
|
|
subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{
|
|
Channel: channel,
|
|
Currency: enabledCurrencies[j],
|
|
})
|
|
}
|
|
l.Websocket.SubscribeToChannels(subscriptions)
|
|
}
|
|
|
|
// Subscribe sends a websocket message to receive data from the channel
|
|
func (l *LakeBTC) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error {
|
|
return l.WebsocketConn.Client.Subscribe(channelToSubscribe.Channel)
|
|
}
|
|
|
|
// wsHandleIncomingData services incoming data from the websocket connection
|
|
func (l *LakeBTC) wsHandleIncomingData() {
|
|
l.Websocket.Wg.Add(1)
|
|
defer l.Websocket.Wg.Done()
|
|
for {
|
|
select {
|
|
case <-l.Websocket.ShutdownC:
|
|
return
|
|
case data := <-l.WebsocketConn.Ticker:
|
|
if l.Verbose {
|
|
log.Debugf(log.ExchangeSys,
|
|
"%v Websocket message received: %v", l.Name, data)
|
|
}
|
|
l.Websocket.TrafficAlert <- struct{}{}
|
|
err := l.processTicker(data.Data)
|
|
if err != nil {
|
|
l.Websocket.DataHandler <- err
|
|
return
|
|
}
|
|
case data := <-l.WebsocketConn.Trade:
|
|
l.Websocket.TrafficAlert <- struct{}{}
|
|
if l.Verbose {
|
|
log.Debugf(log.ExchangeSys,
|
|
"%v Websocket message received: %v", l.Name, data)
|
|
}
|
|
err := l.processTrades(data.Data, data.Channel)
|
|
if err != nil {
|
|
l.Websocket.DataHandler <- err
|
|
return
|
|
}
|
|
case data := <-l.WebsocketConn.Orderbook:
|
|
l.Websocket.TrafficAlert <- struct{}{}
|
|
if l.Verbose {
|
|
log.Debugf(log.ExchangeSys,
|
|
"%v Websocket message received: %v", l.Name, data)
|
|
}
|
|
err := l.processOrderbook(data.Data, data.Channel)
|
|
if err != nil {
|
|
l.Websocket.DataHandler <- err
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (l *LakeBTC) processTrades(data, channel string) error {
|
|
var tradeData WsTrades
|
|
err := common.JSONDecode([]byte(data), &tradeData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
curr := l.getCurrencyFromChannel(channel)
|
|
for i := 0; i < len(tradeData.Trades); i++ {
|
|
l.Websocket.DataHandler <- wshandler.TradeData{
|
|
Timestamp: time.Unix(tradeData.Trades[i].Date, 0),
|
|
CurrencyPair: curr,
|
|
AssetType: asset.Spot,
|
|
Exchange: l.GetName(),
|
|
EventType: asset.Spot.String(),
|
|
EventTime: tradeData.Trades[i].Date,
|
|
Price: tradeData.Trades[i].Price,
|
|
Amount: tradeData.Trades[i].Amount,
|
|
Side: tradeData.Trades[i].Type,
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (l *LakeBTC) processOrderbook(obUpdate, channel string) error {
|
|
var update WsOrderbookUpdate
|
|
err := common.JSONDecode([]byte(obUpdate), &update)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
book := orderbook.Base{
|
|
Pair: l.getCurrencyFromChannel(channel),
|
|
LastUpdated: time.Now(),
|
|
AssetType: asset.Spot,
|
|
ExchangeName: l.Name,
|
|
}
|
|
|
|
for i := 0; i < len(update.Asks); i++ {
|
|
var amount, price float64
|
|
amount, err = strconv.ParseFloat(update.Asks[i][1], 64)
|
|
if err != nil {
|
|
l.Websocket.DataHandler <- fmt.Errorf("%v error parsing ticker data 'low' %v", l.Name, update.Asks[i])
|
|
continue
|
|
}
|
|
price, err = strconv.ParseFloat(update.Asks[i][0], 64)
|
|
if err != nil {
|
|
l.Websocket.DataHandler <- fmt.Errorf("%v error parsing orderbook price %v", l.Name, update.Asks[i])
|
|
continue
|
|
}
|
|
book.Asks = append(book.Asks, orderbook.Item{
|
|
Amount: amount,
|
|
Price: price,
|
|
})
|
|
}
|
|
for i := 0; i < len(update.Bids); i++ {
|
|
var amount, price float64
|
|
amount, err = strconv.ParseFloat(update.Bids[i][1], 64)
|
|
if err != nil {
|
|
l.Websocket.DataHandler <- fmt.Errorf("%v error parsing ticker data 'low' %v", l.Name, update.Bids[i])
|
|
continue
|
|
}
|
|
price, err = strconv.ParseFloat(update.Bids[i][0], 64)
|
|
if err != nil {
|
|
l.Websocket.DataHandler <- fmt.Errorf("%v error parsing orderbook price %v", l.Name, update.Bids[i])
|
|
continue
|
|
}
|
|
book.Bids = append(book.Bids, orderbook.Item{
|
|
Amount: amount,
|
|
Price: price,
|
|
})
|
|
}
|
|
return l.Websocket.Orderbook.LoadSnapshot(&book, true)
|
|
}
|
|
|
|
func (l *LakeBTC) getCurrencyFromChannel(channel string) currency.Pair {
|
|
curr := strings.Replace(channel, marketSubstring, "", 1)
|
|
curr = strings.Replace(curr, globalSubstring, "", 1)
|
|
return currency.NewPairFromString(curr)
|
|
}
|
|
|
|
func (l *LakeBTC) processTicker(ticker string) error {
|
|
var tUpdate map[string]interface{}
|
|
err := common.JSONDecode([]byte(ticker), &tUpdate)
|
|
if err != nil {
|
|
l.Websocket.DataHandler <- err
|
|
return err
|
|
}
|
|
for k, v := range tUpdate {
|
|
tickerData := v.(map[string]interface{})
|
|
processTickerItem := func(tick map[string]interface{}, item string) float64 {
|
|
if tick[item] == nil {
|
|
return 0
|
|
}
|
|
|
|
p, err := strconv.ParseFloat(tick[item].(string), 64)
|
|
if err != nil {
|
|
l.Websocket.DataHandler <- fmt.Errorf("%s error parsing ticker data '%s' %v", l.Name, item, tickerData)
|
|
return 0
|
|
}
|
|
|
|
return p
|
|
}
|
|
|
|
l.Websocket.DataHandler <- wshandler.TickerData{
|
|
Exchange: l.Name,
|
|
Bid: processTickerItem(tickerData, tickerBuyString),
|
|
High: processTickerItem(tickerData, tickerHighString),
|
|
Last: processTickerItem(tickerData, tickerLastString),
|
|
Low: processTickerItem(tickerData, tickerLowString),
|
|
Ask: processTickerItem(tickerData, tickerSellString),
|
|
Volume: processTickerItem(tickerData, tickerVolumeString),
|
|
AssetType: asset.Spot,
|
|
Pair: currency.NewPairFromString(k),
|
|
}
|
|
}
|
|
return nil
|
|
}
|