Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

exchanges: Add Futures WebSocket streaming and Spot WebSocket API #1446

Open
wants to merge 41 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
f28f84e
Adding usdt future websocket functions
samuael Dec 17, 2023
30abd30
Adding USDT Futures websocket handlers
samuael Dec 19, 2023
a6c765a
Completed USDT websocket support
samuael Dec 21, 2023
a4dd269
Merge branch 'master' of http://github.com/thrasher-corp/gocryptotrad…
samuael Dec 21, 2023
fff3ede
Types and Convert methods update
samuael Dec 21, 2023
526f149
Adding cfutures websocket support
samuael Dec 22, 2023
37af6ff
Adding coin margined futures websocket support
samuael Dec 23, 2023
226c8f5
Completed cfutures websocket handlers
samuael Dec 24, 2023
45ed9f3
Adding Websocket API endpoints
samuael Dec 24, 2023
13ca144
Adding websocket API endpoints and connection method
samuael Dec 24, 2023
ee4abc3
Adding Market websocket API endpoints
samuael Dec 25, 2023
6636d57
Completed Market Public Endpoints and unit tests
samuael Dec 26, 2023
cb02f0c
Merge branch 'master' of http://github.com/thrasher-corp/gocryptotrad…
samuael Dec 27, 2023
4d1a16b
Merge branch 'master' of http://github.com/thrasher-corp/gocryptotrad…
samuael Dec 31, 2023
eeebf40
Merge branch 'master' of http://github.com/thrasher-corp/gocryptotrad…
samuael Jan 2, 2024
cff7cd1
Adding websocket API trade endpoints
samuael Jan 4, 2024
d6f302e
Adding Trade endpoints
samuael Jan 6, 2024
3d37efb
Completed adding Trading endpoints
samuael Jan 7, 2024
bfdd16a
types, documentation, and endpoints update
samuael Jan 8, 2024
9fc1e1d
Authentication and signing method update
samuael Jan 9, 2024
85014fa
Merge branch 'master' of http://github.com/thrasher-corp/gocryptotrad…
samuael Jan 9, 2024
1db9d8c
Added account and user data stream requests
samuael Jan 10, 2024
2867ce4
Adding Websocket SPOT API endpoints to wrapper functions
samuael Jan 11, 2024
55c4b1c
Merge branch 'master' of http://github.com/thrasher-corp/gocryptotrad…
samuael Jan 11, 2024
b497ea9
Finalize websocket API endpoints to wrapper integration
samuael Jan 12, 2024
873fd28
Testing and fixing API Websocket endpoints
samuael Jan 13, 2024
d5571f8
Merge branch 'master' of http://github.com/thrasher-corp/gocryptotrad…
samuael Jan 13, 2024
67a14e3
Merge branch 'master' of http://github.com/thrasher-corp/gocryptotrad…
samuael Jan 16, 2024
08346a7
Merge branch 'master' of http://github.com/thrasher-corp/gocryptotrad…
samuael Feb 13, 2024
cade5bf
Merge branch 'master' of http://github.com/thrasher-corp/gocryptotrad…
samuael Feb 19, 2024
432632e
Update unit tests and fix linter issues
samuael Feb 19, 2024
da09b8e
Merge branch 'master' of http://github.com/thrasher-corp/gocryptotrad…
samuael Feb 21, 2024
7b58558
codespell fix
samuael Feb 21, 2024
658de98
unit tests update
samuael Feb 28, 2024
82280f9
Merge branch 'master' of http://github.com/thrasher-corp/gocryptotrad…
samuael Feb 28, 2024
b0168ee
Merge branch 'master' of http://github.com/thrasher-corp/gocryptotrad…
samuael Mar 13, 2024
ea862b0
Merge branch 'master' of http://github.com/thrasher-corp/gocryptotrad…
samuael Mar 23, 2024
eada24d
Adding European Options websocket support
samuael Mar 30, 2024
ab04442
Merge branch 'master' of http://github.com/thrasher-corp/gocryptotrad…
samuael Mar 30, 2024
2c19072
adding websocket handlers
samuael Apr 1, 2024
2c6a1e9
Completed adding european options public stream handlers
samuael Apr 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ shazbert | https://github.com/shazbert
dependabot[bot] | https://github.com/apps/dependabot
gloriousCode | https://github.com/gloriousCode
dependabot-preview[bot] | https://github.com/apps/dependabot-preview
xtda | https://github.com/xtda
gbjk | https://github.com/gbjk
xtda | https://github.com/xtda
lrascao | https://github.com/lrascao
Rots | https://github.com/Rots
vazha | https://github.com/vazha
ydm | https://github.com/ydm
vazha | https://github.com/vazha
Rots | https://github.com/Rots
ermalguni | https://github.com/ermalguni
MadCozBadd | https://github.com/MadCozBadd
Beadko | https://github.com/Beadko
vadimzhukck | https://github.com/vadimzhukck
140am | https://github.com/140am
marcofranssen | https://github.com/marcofranssen
geseq | https://github.com/geseq
marcofranssen | https://github.com/marcofranssen
140am | https://github.com/140am
samuael | https://github.com/samuael
TaltaM | https://github.com/TaltaM
dackroyd | https://github.com/dackroyd
Expand All @@ -26,10 +26,10 @@ khcchiu | https://github.com/khcchiu
yangrq1018 | https://github.com/yangrq1018
woshidama323 | https://github.com/woshidama323
crackcomm | https://github.com/crackcomm
azhang | https://github.com/azhang
andreygrehov | https://github.com/andreygrehov
bretep | https://github.com/bretep
Christian-Achilli | https://github.com/Christian-Achilli
mshogin | https://github.com/mshogin
herenow | https://github.com/herenow
tk42 | https://github.com/tk42
soxipy | https://github.com/soxipy
cornelk | https://github.com/cornelk
gam-phon | https://github.com/gam-phon
herenow | https://github.com/herenow
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2014-2023 The GoCryptoTrader Developers
Copyright (c) 2014-2024 The GoCryptoTrader Developers

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,19 +146,20 @@ Binaries will be published once the codebase reaches a stable condition.
| [dependabot[bot]](https://github.com/apps/dependabot) | 228 |
| [gloriousCode](https://github.com/gloriousCode) | 224 |
| [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 88 |
| [gbjk](https://github.com/gbjk) | 51 |
| [xtda](https://github.com/xtda) | 47 |
| [gbjk](https://github.com/gbjk) | 42 |
| [lrascao](https://github.com/lrascao) | 27 |
| [Rots](https://github.com/Rots) | 15 |
| [vazha](https://github.com/vazha) | 15 |
| [ydm](https://github.com/ydm) | 15 |
| [vazha](https://github.com/vazha) | 15 |
| [Rots](https://github.com/Rots) | 15 |
| [ermalguni](https://github.com/ermalguni) | 14 |
| [MadCozBadd](https://github.com/MadCozBadd) | 13 |
| [Beadko](https://github.com/Beadko) | 11 |
| [vadimzhukck](https://github.com/vadimzhukck) | 10 |
| [140am](https://github.com/140am) | 8 |
| [marcofranssen](https://github.com/marcofranssen) | 8 |
| [geseq](https://github.com/geseq) | 8 |
| [marcofranssen](https://github.com/marcofranssen) | 8 |
| [140am](https://github.com/140am) | 8 |
| [samuael](https://github.com/samuael) | 7 |
| [TaltaM](https://github.com/TaltaM) | 6 |
| [dackroyd](https://github.com/dackroyd) | 5 |
Expand All @@ -167,10 +168,10 @@ Binaries will be published once the codebase reaches a stable condition.
| [yangrq1018](https://github.com/yangrq1018) | 4 |
| [woshidama323](https://github.com/woshidama323) | 3 |
| [crackcomm](https://github.com/crackcomm) | 3 |
| [azhang](https://github.com/azhang) | 2 |
| [andreygrehov](https://github.com/andreygrehov) | 2 |
| [bretep](https://github.com/bretep) | 2 |
| [Christian-Achilli](https://github.com/Christian-Achilli) | 2 |
| [mshogin](https://github.com/mshogin) | 2 |
| [herenow](https://github.com/herenow) | 2 |
| [tk42](https://github.com/tk42) | 2 |
| [soxipy](https://github.com/soxipy) | 2 |
| [cornelk](https://github.com/cornelk) | 2 |
| [gam-phon](https://github.com/gam-phon) | 2 |
| [herenow](https://github.com/herenow) | 2 |
Expand Down
2 changes: 1 addition & 1 deletion config_example.json
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@
},
"enabled": {
"autoPairUpdates": true,
"websocketAPI": false
"websocketAPI": true
}
},
"bankAccounts": [
Expand Down
135 changes: 45 additions & 90 deletions exchanges/binance/binance.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,31 @@ import (
"sort"
"strconv"
"strings"
"sync"
"time"

"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/convert"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/exchanges/subscription"
"github.com/thrasher-corp/gocryptotrader/types"
)

// Binance is the overarching type across the Binance package
type Binance struct {
exchange.Base
// Valid string list that is required by the exchange
validLimits []int
validLimits []int64
obm *orderbookManager

// isAPIStreamConnected is true if the spot API stream websocket connection is established
isAPIStreamConnected bool

isAPIStreamConnectionLock sync.Mutex
}

const (
Expand Down Expand Up @@ -108,6 +114,10 @@ var (
errOrderIDMustBeSet = errors.New("orderID must be set")
errAmountMustBeSet = errors.New("amount must not be <= 0")
errEitherLoanOrCollateralAmountsMustBeSet = errors.New("either loan or collateral amounts must be set")
errNilArgument = errors.New("nil argument")
errWebsocketAPICallNotEnabled = errors.New("websocket API connection not enabled")
errTimestampInfoRequired = errors.New("timestamp information is required")
errListenKeyIsRequired = errors.New("listen key is required")
)

var subscriptionNames = map[string]string{
Expand Down Expand Up @@ -141,7 +151,7 @@ func (b *Binance) GetOrderBook(ctx context.Context, obd OrderBookDataRequestPara
return nil, err
}
params.Set("symbol", symbol)
params.Set("limit", strconv.Itoa(obd.Limit))
params.Set("limit", strconv.FormatInt(obd.Limit, 10))

var resp OrderBookData
if err := b.SendHTTPRequest(ctx,
Expand All @@ -157,39 +167,17 @@ func (b *Binance) GetOrderBook(ctx context.Context, obd OrderBookDataRequestPara
LastUpdateID: resp.LastUpdateID,
}
for x := range resp.Bids {
price, err := strconv.ParseFloat(resp.Bids[x][0], 64)
if err != nil {
return nil, err
}

amount, err := strconv.ParseFloat(resp.Bids[x][1], 64)
if err != nil {
return nil, err
}

orderbook.Bids[x] = OrderbookItem{
Price: price,
Quantity: amount,
Price: resp.Bids[x][0].Float64(),
Quantity: resp.Bids[x][1].Float64(),
}
}

for x := range resp.Asks {
price, err := strconv.ParseFloat(resp.Asks[x][0], 64)
if err != nil {
return nil, err
}

amount, err := strconv.ParseFloat(resp.Asks[x][1], 64)
if err != nil {
return nil, err
}

orderbook.Asks[x] = OrderbookItem{
Price: price,
Quantity: amount,
Price: resp.Asks[x][0].Float64(),
Quantity: resp.Asks[x][1].Float64(),
}
}

return &orderbook, nil
}

Expand All @@ -202,7 +190,7 @@ func (b *Binance) GetMostRecentTrades(ctx context.Context, rtr RecentTradeReques
return nil, err
}
params.Set("symbol", symbol)
params.Set("limit", strconv.Itoa(rtr.Limit))
params.Set("limit", strconv.FormatInt(rtr.Limit, 10))

path := recentTrades + "?" + params.Encode()

Expand Down Expand Up @@ -273,7 +261,7 @@ func (b *Binance) GetUserMarginInterestHistory(ctx context.Context, assetCurrenc
// https://binance-docs.github.io/apidocs/spot/en/#compressed-aggregate-trades-list
func (b *Binance) GetAggregatedTrades(ctx context.Context, arg *AggregatedTradeRequestParams) ([]AggregatedTrade, error) {
params := url.Values{}
params.Set("symbol", arg.Symbol.String())
params.Set("symbol", arg.Symbol)
// If the user request is directly not supported by the exchange, we might be able to fulfill it
// by merging results from multiple API requests
needBatch := true // Need to batch unless user has specified a limit
Expand Down Expand Up @@ -368,7 +356,7 @@ func (b *Binance) batchAggregateTrades(ctx context.Context, arg *AggregatedTrade
if !arg.EndTime.IsZero() {
// get index for truncating to end time
lastIndex = sort.Search(len(additionalTrades), func(i int) bool {
return arg.EndTime.Before(additionalTrades[i].TimeStamp)
return arg.EndTime.Before(additionalTrades[i].TimeStamp.Time())
})
}
// don't include the first as the request was inclusive from last ATradeID
Expand Down Expand Up @@ -404,7 +392,7 @@ func (b *Binance) GetSpotKline(ctx context.Context, arg *KlinesRequestParams) ([
params.Set("symbol", symbol)
params.Set("interval", arg.Interval)
if arg.Limit != 0 {
params.Set("limit", strconv.Itoa(arg.Limit))
params.Set("limit", strconv.FormatInt(arg.Limit, 10))
}
if !arg.StartTime.IsZero() {
params.Set("startTime", timeString(arg.StartTime))
Expand All @@ -414,8 +402,7 @@ func (b *Binance) GetSpotKline(ctx context.Context, arg *KlinesRequestParams) ([
}

path := candleStick + "?" + params.Encode()
var resp interface{}

var resp [][]types.Number
err = b.SendHTTPRequest(ctx,
exchange.RestSpotSupplementary,
path,
Expand All @@ -424,55 +411,24 @@ func (b *Binance) GetSpotKline(ctx context.Context, arg *KlinesRequestParams) ([
if err != nil {
return nil, err
}
responseData, ok := resp.([]interface{})
if !ok {
return nil, common.GetTypeAssertError("[]interface{}", resp)
}

klineData := make([]CandleStick, len(responseData))
for x := range responseData {
individualData, ok := responseData[x].([]interface{})
if !ok {
return nil, common.GetTypeAssertError("[]interface{}", responseData[x])
}
if len(individualData) != 12 {
klineData := make([]CandleStick, len(resp))
for x := range resp {
if len(resp[x]) != 12 {
return nil, errors.New("unexpected kline data length")
}
var candle CandleStick
if candle.OpenTime, err = convert.TimeFromUnixTimestampFloat(individualData[0]); err != nil {
return nil, err
}
if candle.Open, err = convert.FloatFromString(individualData[1]); err != nil {
return nil, err
}
if candle.High, err = convert.FloatFromString(individualData[2]); err != nil {
return nil, err
}
if candle.Low, err = convert.FloatFromString(individualData[3]); err != nil {
return nil, err
klineData[x] = CandleStick{
OpenTime: time.UnixMilli(resp[x][0].Int64()),
Open: resp[x][1].Float64(),
High: resp[x][2].Float64(),
Low: resp[x][3].Float64(),
Close: resp[x][4].Float64(),
Volume: resp[x][5].Float64(),
CloseTime: time.UnixMilli(resp[x][6].Int64()),
QuoteAssetVolume: resp[x][7].Float64(),
TradeCount: resp[x][8].Float64(),
TakerBuyAssetVolume: resp[x][9].Float64(),
TakerBuyQuoteAssetVolume: resp[x][10].Float64(),
}
if candle.Close, err = convert.FloatFromString(individualData[4]); err != nil {
return nil, err
}
if candle.Volume, err = convert.FloatFromString(individualData[5]); err != nil {
return nil, err
}
if candle.CloseTime, err = convert.TimeFromUnixTimestampFloat(individualData[6]); err != nil {
return nil, err
}
if candle.QuoteAssetVolume, err = convert.FloatFromString(individualData[7]); err != nil {
return nil, err
}
if candle.TradeCount, ok = individualData[8].(float64); !ok {
return nil, common.GetTypeAssertError("float64", individualData[8])
}
if candle.TakerBuyAssetVolume, err = convert.FloatFromString(individualData[9]); err != nil {
return nil, err
}
if candle.TakerBuyQuoteAssetVolume, err = convert.FloatFromString(individualData[10]); err != nil {
return nil, err
}
klineData[x] = candle
}
return klineData, nil
}
Expand Down Expand Up @@ -648,8 +604,8 @@ func (b *Binance) CancelExistingOrder(ctx context.Context, symbol currency.Pair,
// OpenOrders Current open orders. Get all open orders on a symbol.
// Careful when accessing this with no symbol: The number of requests counted
// against the rate limiter is significantly higher
func (b *Binance) OpenOrders(ctx context.Context, pair currency.Pair) ([]QueryOrderData, error) {
var resp []QueryOrderData
func (b *Binance) OpenOrders(ctx context.Context, pair currency.Pair) ([]TradeOrder, error) {
var resp []TradeOrder
params := url.Values{}
var p string
var err error
Expand Down Expand Up @@ -680,8 +636,8 @@ func (b *Binance) OpenOrders(ctx context.Context, pair currency.Pair) ([]QueryOr
// AllOrders Get all account orders; active, canceled, or filled.
// orderId optional param
// limit optional param, default 500; max 500
func (b *Binance) AllOrders(ctx context.Context, symbol currency.Pair, orderID, limit string) ([]QueryOrderData, error) {
var resp []QueryOrderData
func (b *Binance) AllOrders(ctx context.Context, symbol currency.Pair, orderID, limit string) ([]TradeOrder, error) {
var resp []TradeOrder

params := url.Values{}
symbolValue, err := b.FormatSymbol(symbol, asset.Spot)
Expand All @@ -708,9 +664,8 @@ func (b *Binance) AllOrders(ctx context.Context, symbol currency.Pair, orderID,
}

// QueryOrder returns information on a past order
func (b *Binance) QueryOrder(ctx context.Context, symbol currency.Pair, origClientOrderID string, orderID int64) (QueryOrderData, error) {
var resp QueryOrderData

func (b *Binance) QueryOrder(ctx context.Context, symbol currency.Pair, origClientOrderID string, orderID int64) (TradeOrder, error) {
var resp TradeOrder
params := url.Values{}
symbolValue, err := b.FormatSymbol(symbol, asset.Spot)
if err != nil {
Expand Down Expand Up @@ -894,7 +849,7 @@ func (b *Binance) SendAuthHTTPRequest(ctx context.Context, ePath exchange.URL, m
}

// CheckLimit checks value against a variable list
func (b *Binance) CheckLimit(limit int) error {
func (b *Binance) CheckLimit(limit int64) error {
for x := range b.validLimits {
if b.validLimits[x] == limit {
return nil
Expand All @@ -905,7 +860,7 @@ func (b *Binance) CheckLimit(limit int) error {

// SetValues sets the default valid values
func (b *Binance) SetValues() {
b.validLimits = []int{5, 10, 20, 50, 100, 500, 1000, 5000}
b.validLimits = []int64{5, 10, 20, 50, 100, 500, 1000, 5000}
}

// GetFee returns an estimate of fee based on type of transaction
Expand Down
Loading
Loading