Ver Fonte

Initial

jkalstad há 8 anos atrás
commit
903d3a1cbf
2 ficheiros alterados com 276 adições e 0 exclusões
  1. 49 0
      README.md
  2. 227 0
      binance.go

+ 49 - 0
README.md

@@ -0,0 +1,49 @@
+# go-binance
+
+>right now it's only the XRP and BTC pairs - more coming I swear
+
+Beginning of a library to interact with binance
+
+
+```
+package main
+
+import (
+	"fmt"
+	"os"
+	"os/signal"
+	"syscall"
+    "github.com/JakeKalstad/go-binance"
+)
+
+func main() {
+	osSignals := make(chan os.Signal, 1)
+	signal.Notify(
+		osSignals,
+		syscall.SIGINT,
+		syscall.SIGTERM,
+		syscall.SIGKILL,
+		syscall.SIGQUIT,
+	)
+	binanceStreamer := binance.NewBinanceStream(binance.Options{Symbol: binance.XRPBTC})
+	dCh := binanceStreamer.GetDepthEventChannel()
+	errCh := binanceStreamer.GetErrorChannel()
+	binanceStreamer.Start()
+	for {
+		select {
+		case evt := <-dCh:
+			fmt.Printf("\nReceived a new depth event: %+v\n\n", evt)
+			break
+		case err := <-errCh:
+			fmt.Printf("\nReceived an error %+s\n\n", err)
+			panic(err)
+		case sig := <-osSignals:
+			fmt.Printf("Os told me to: %s", sig)
+			return
+		}
+	}
+}
+```
+
+
+### Enjoy

+ 227 - 0
binance.go

@@ -0,0 +1,227 @@
+package binance
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+
+	"golang.org/x/net/websocket"
+)
+
+const (
+	XRPBTC = "xrpbtc"
+)
+
+type Options struct {
+	Symbol        string
+	LookbackLimit int
+}
+
+func NewBinanceStream(opts Options) BinanceStream {
+	return &binanceStream{
+		Options:           opts,
+		LastUpdateID:      -1,
+		DepthEventChannel: make(chan DepthEvent),
+		ErrorChannel:      make(chan error),
+	}
+}
+
+type binanceStream struct {
+	Options           Options
+	LastUpdateID      int64
+	DepthEventChannel chan DepthEvent
+	ErrorChannel      chan error
+}
+
+type BinanceStream interface {
+	Start()
+	GetDepthEventChannel() chan DepthEvent
+	GetErrorChannel() chan error
+}
+
+type BidCount struct {
+	Price  string
+	Volume string
+}
+
+type Bids []*BidCount
+
+type DepthEvent struct {
+	EventType     string
+	EventTime     int64
+	Symbol        string
+	FirstUpdateID int64
+	FinalUpdateID int64
+	BidsToUpdate  Bids
+	AsksToUpdate  Bids
+}
+
+type rawBids [][]interface{}
+type depth struct {
+	LastUpdateID int64   `json:"lastUpdateId"`
+	Bids         rawBids `json:"bids"`
+}
+
+type depthEvent struct {
+	EventType     string  `json:"e"`
+	EventTime     int64   `json:"E"`
+	Symbol        string  `json:"s"`
+	FirstUpdateID int64   `json:"U"`
+	FinalUpdateID int64   `json:"u"`
+	BidsToUpdate  rawBids `json:"b"`
+	AsksToUpdate  rawBids `json:"a"`
+}
+
+func (b *binanceStream) GetDepthEventChannel() chan DepthEvent {
+	return b.DepthEventChannel
+}
+
+func (b *binanceStream) GetErrorChannel() chan error {
+	return b.ErrorChannel
+}
+
+func (b *binanceStream) Start() {
+	go func() {
+		e := b.getDepth(b.Options.Symbol, 1000)
+		if e != nil {
+			b.ErrorChannel <- e
+		}
+		b.processDepthEvents()
+	}()
+}
+
+func transformRawBids(rawBids rawBids) Bids {
+	bids := Bids{}
+	for _, bSet := range rawBids {
+		price := ""
+		vol := ""
+		for idx, rBid := range bSet {
+			val, ok := rBid.(string)
+			if ok {
+				if idx == 0 {
+					price = val
+				}
+				if idx == 1 {
+					vol = val
+				}
+			}
+		}
+		bids = append(bids, &BidCount{
+			Price:  price,
+			Volume: vol,
+		})
+	}
+	return bids
+}
+
+func (b *binanceStream) processTradeEvents() {
+	origin := "http://localhost/"
+	url := b.getWebsocketURL("trade")
+	ws, err := websocket.Dial(url, "", origin)
+	if err != nil {
+		b.ErrorChannel <- err
+		return
+	}
+	if _, err := ws.Write(nil); err != nil {
+		b.ErrorChannel <- err
+		return
+	}
+	var msg = make([]byte, 4096)
+	var n int
+	n, err = ws.Read(msg)
+	if err != nil {
+		b.ErrorChannel <- err
+		return
+	}
+	for err == nil {
+		evt := depthEvent{}
+		e := json.Unmarshal(msg[:n], &evt)
+		if e != nil {
+			b.ErrorChannel <- e
+			return
+		}
+		if evt.FinalUpdateID > b.LastUpdateID {
+			b.DepthEventChannel <- DepthEvent{
+				EventType:     evt.EventType,
+				EventTime:     evt.EventTime,
+				Symbol:        evt.Symbol,
+				FirstUpdateID: evt.FirstUpdateID,
+				FinalUpdateID: evt.FinalUpdateID,
+				BidsToUpdate:  transformRawBids(evt.BidsToUpdate),
+				AsksToUpdate:  transformRawBids(evt.AsksToUpdate),
+			}
+		}
+		n, err = ws.Read(msg)
+	}
+}
+
+func (b *binanceStream) getWebsocketURL(view string) string {
+	return fmt.Sprintf("wss://stream.binance.com:9443/ws/%s@%s", b.Options.Symbol, view)
+}
+
+func (b *binanceStream) processDepthEvents() {
+	origin := "http://localhost/"
+	url := b.getWebsocketURL("depth")
+	ws, err := websocket.Dial(url, "", origin)
+	if err != nil {
+		fmt.Println(err)
+		b.ErrorChannel <- err
+		return
+	}
+	if _, err := ws.Write(nil); err != nil {
+		b.ErrorChannel <- err
+		return
+	}
+	var msg = make([]byte, 4096)
+	var n int
+	n, err = ws.Read(msg)
+	if err != nil {
+		b.ErrorChannel <- err
+		return
+	}
+	for err == nil {
+		evt := depthEvent{}
+		e := json.Unmarshal(msg[:n], &evt)
+		if e != nil {
+			fmt.Println(msg)
+			b.ErrorChannel <- e
+			return
+		}
+		if evt.FinalUpdateID > b.LastUpdateID {
+			b.DepthEventChannel <- DepthEvent{
+				EventType:     evt.EventType,
+				EventTime:     evt.EventTime,
+				Symbol:        evt.Symbol,
+				FirstUpdateID: evt.FirstUpdateID,
+				FinalUpdateID: evt.FinalUpdateID,
+				BidsToUpdate:  transformRawBids(evt.BidsToUpdate),
+				AsksToUpdate:  transformRawBids(evt.AsksToUpdate),
+			}
+		}
+		n, err = ws.Read(msg)
+	}
+}
+
+func (b *binanceStream) getDepth(symbol string, limit int) error {
+	req, err := http.NewRequest("GET", fmt.Sprintf("https://www.binance.com/api/v1/depth?symbol=%s&limit=%d", symbol, limit), nil)
+	if err != nil {
+		return err
+	}
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+	rawData, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return err
+	}
+	depth := &depth{}
+	err = json.Unmarshal(rawData, depth)
+	if err != nil {
+		return err
+	}
+	b.LastUpdateID = depth.LastUpdateID
+	return nil
+}