80 lines
1.8 KiB
Go
80 lines
1.8 KiB
Go
package exchange
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type BinanceWS struct {
|
|
Tracked []string
|
|
}
|
|
|
|
func NewBinanceWS(tracked []string) *BinanceWS {
|
|
return &BinanceWS{Tracked: tracked}
|
|
}
|
|
// Run connects to Binance WS and streams bookTicker data.
|
|
func (b *BinanceWS) Run(updateFn func(coin string, price, bid, ask float64)) error {
|
|
streams := ""
|
|
for i, sym := range b.Tracked {
|
|
if i > 0 {
|
|
streams += "/"
|
|
}
|
|
streams += fmt.Sprintf("%s@bookTicker", strings.ToLower(sym))
|
|
}
|
|
url := fmt.Sprintf("wss://fstream.binance.com/stream?streams=%s", streams)
|
|
|
|
conn := NewPriceConnector(url, "Binance", 120*time.Second, 30*time.Second)
|
|
conn.PingInterval = 45 * time.Second
|
|
|
|
conn.OnConnect = func() {
|
|
log.Printf("[Binance WS] Connected")
|
|
}
|
|
|
|
conn.OnMessage = func(msg []byte) {
|
|
// Combined stream: {"stream":"...","data":{...}}
|
|
// Navigate through "data" using map to avoid field name conflicts
|
|
var raw map[string]json.RawMessage
|
|
if err := json.Unmarshal(msg, &raw); err != nil {
|
|
return
|
|
}
|
|
dataRaw, ok := raw["data"]
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
// Parse data object as flat map to extract fields by exact name
|
|
var dataMap map[string]interface{}
|
|
if err := json.Unmarshal(dataRaw, &dataMap); err != nil {
|
|
return
|
|
}
|
|
|
|
symbol, _ := dataMap["s"].(string)
|
|
bidStr, _ := dataMap["b"].(string)
|
|
askStr, _ := dataMap["a"].(string)
|
|
if symbol == "" || bidStr == "" || askStr == "" {
|
|
return
|
|
}
|
|
|
|
bid, err1 := strconv.ParseFloat(bidStr, 64)
|
|
ask, err2 := strconv.ParseFloat(askStr, 64)
|
|
if err1 != nil || err2 != nil || bid <= 0 || ask <= 0 {
|
|
return
|
|
}
|
|
|
|
// Extract coin name (e.g., "BTCUSDT" -> "BTC")
|
|
coin := symbolToCoin(symbol, "USDT")
|
|
if coin == "" {
|
|
return
|
|
}
|
|
|
|
mid := (bid + ask) / 2.0
|
|
updateFn(coin, mid, bid, ask)
|
|
}
|
|
|
|
return conn.Run()
|
|
}
|