mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 15:09:42 +00:00
orderbook: consolidate slice array types to orderbook package (#1992)
* orderbook: consolidate slice array types to orderbook package * Update exchanges/bybit/bybit_types.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * linter: fix and add test * cranktakular: nits * cranktakular: nits * Update exchanges/orderbook/orderbook_types.go Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com> * Update exchanges/gateio/gateio_test.go Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com> * gk: nits consolidation * gk: rm unifySpotOrderbook func * gk: nit but different * linter: fix * gk: nits * glorious: nits * Update exchanges/binance/binance.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * Update exchanges/binance/binance_cfutures.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * Update exchanges/binanceus/binanceus.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * thrasher-:nits * thrasher-: more nit --------- Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com> Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>
This commit is contained in:
@@ -118,40 +118,17 @@ func (e *Exchange) GetExchangeInfo(ctx context.Context) (ExchangeInfo, error) {
|
||||
}
|
||||
|
||||
// GetOrderBook returns full orderbook information
|
||||
//
|
||||
// OrderBookDataRequestParams contains the following members
|
||||
// symbol: string of currency pair
|
||||
// limit: returned limit amount
|
||||
func (e *Exchange) GetOrderBook(ctx context.Context, obd OrderBookDataRequestParams) (*OrderBook, error) {
|
||||
params := url.Values{}
|
||||
symbol, err := e.FormatSymbol(obd.Symbol, asset.Spot)
|
||||
func (e *Exchange) GetOrderBook(ctx context.Context, pair currency.Pair, limit uint64) (*OrderBookResponse, error) {
|
||||
symbol, err := e.FormatSymbol(pair, asset.Spot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
params.Set("limit", strconv.Itoa(obd.Limit))
|
||||
params.Set("limit", strconv.FormatUint(limit, 10))
|
||||
|
||||
var resp *OrderBookData
|
||||
if err := e.SendHTTPRequest(ctx, exchange.RestSpotSupplementary, common.EncodeURLValues(orderBookDepth, params), orderbookLimit(obd.Limit), &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob := &OrderBook{
|
||||
Bids: make([]OrderbookItem, len(resp.Bids)),
|
||||
Asks: make([]OrderbookItem, len(resp.Asks)),
|
||||
LastUpdateID: resp.LastUpdateID,
|
||||
}
|
||||
for x := range resp.Bids {
|
||||
ob.Bids[x].Price = resp.Bids[x][0].Float64()
|
||||
ob.Bids[x].Quantity = resp.Bids[x][1].Float64()
|
||||
}
|
||||
|
||||
for x := range resp.Asks {
|
||||
ob.Asks[x].Price = resp.Asks[x][0].Float64()
|
||||
ob.Asks[x].Quantity = resp.Asks[x][1].Float64()
|
||||
}
|
||||
|
||||
return ob, nil
|
||||
var resp *OrderBookResponse
|
||||
return resp, e.SendHTTPRequest(ctx, exchange.RestSpotSupplementary, common.EncodeURLValues(orderBookDepth, params), orderbookLimit(limit), &resp)
|
||||
}
|
||||
|
||||
// GetMostRecentTrades returns recent trade activity
|
||||
|
||||
@@ -84,7 +84,7 @@ func (e *Exchange) FuturesExchangeInfo(ctx context.Context) (CExchangeInfo, erro
|
||||
}
|
||||
|
||||
// GetFuturesOrderbook gets orderbook data for CoinMarginedFutures,
|
||||
func (e *Exchange) GetFuturesOrderbook(ctx context.Context, symbol currency.Pair, limit int64) (*OrderBook, error) {
|
||||
func (e *Exchange) GetFuturesOrderbook(ctx context.Context, symbol currency.Pair, limit uint64) (*OrderBookResponse, error) {
|
||||
symbolValue, err := e.FormatSymbol(symbol, asset.CoinMarginedFutures)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -93,7 +93,7 @@ func (e *Exchange) GetFuturesOrderbook(ctx context.Context, symbol currency.Pair
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbolValue)
|
||||
if limit > 0 {
|
||||
params.Set("limit", strconv.FormatInt(limit, 10))
|
||||
params.Set("limit", strconv.FormatUint(limit, 10))
|
||||
}
|
||||
|
||||
rateBudget := cFuturesOrderbook1000Rate
|
||||
@@ -106,27 +106,8 @@ func (e *Exchange) GetFuturesOrderbook(ctx context.Context, symbol currency.Pair
|
||||
rateBudget = cFuturesOrderbook500Rate
|
||||
}
|
||||
|
||||
var data *OrderbookData
|
||||
if err := e.SendHTTPRequest(ctx, exchange.RestCoinMargined, cfuturesOrderbook+params.Encode(), rateBudget, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob := &OrderBook{
|
||||
Bids: make([]OrderbookItem, len(data.Bids)),
|
||||
Asks: make([]OrderbookItem, len(data.Asks)),
|
||||
}
|
||||
|
||||
for x := range data.Bids {
|
||||
ob.Bids[x].Price = data.Bids[x][0].Float64()
|
||||
ob.Bids[x].Quantity = data.Bids[x][1].Float64()
|
||||
}
|
||||
|
||||
for x := range data.Asks {
|
||||
ob.Asks[x].Price = data.Asks[x][0].Float64()
|
||||
ob.Asks[x].Quantity = data.Asks[x][1].Float64()
|
||||
}
|
||||
|
||||
return ob, nil
|
||||
var resp *OrderBookResponse
|
||||
return resp, e.SendHTTPRequest(ctx, exchange.RestCoinMargined, cfuturesOrderbook+params.Encode(), rateBudget, &resp)
|
||||
}
|
||||
|
||||
// GetFuturesPublicTrades gets recent public trades for CoinMarginedFutures,
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/margin"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/subscription"
|
||||
testexch "github.com/thrasher-corp/gocryptotrader/internal/testing/exchange"
|
||||
@@ -1120,14 +1121,8 @@ func TestFetchTradablePairs(t *testing.T) {
|
||||
|
||||
func TestGetOrderBook(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := e.GetOrderBook(t.Context(),
|
||||
OrderBookDataRequestParams{
|
||||
Symbol: currency.NewBTCUSDT(),
|
||||
Limit: 1000,
|
||||
})
|
||||
if err != nil {
|
||||
t.Error("Binance GetOrderBook() error", err)
|
||||
}
|
||||
_, err := e.GetOrderBook(t.Context(), currency.NewBTCUSDT(), 1000)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGetMostRecentTrades(t *testing.T) {
|
||||
@@ -2102,30 +2097,30 @@ func TestWsDepthUpdate(t *testing.T) {
|
||||
require.NoError(t, testexch.Setup(e), "Test instance Setup must not error")
|
||||
e.setupOrderbookManager(t.Context())
|
||||
seedLastUpdateID := int64(161)
|
||||
book := OrderBook{
|
||||
Asks: []OrderbookItem{
|
||||
{Price: 6621.80000000, Quantity: 0.00198100},
|
||||
{Price: 6622.14000000, Quantity: 4.00000000},
|
||||
{Price: 6622.46000000, Quantity: 2.30000000},
|
||||
{Price: 6622.47000000, Quantity: 1.18633300},
|
||||
{Price: 6622.64000000, Quantity: 4.00000000},
|
||||
{Price: 6622.73000000, Quantity: 0.02900000},
|
||||
{Price: 6622.76000000, Quantity: 0.12557700},
|
||||
{Price: 6622.81000000, Quantity: 2.08994200},
|
||||
{Price: 6622.82000000, Quantity: 0.01500000},
|
||||
{Price: 6623.17000000, Quantity: 0.16831300},
|
||||
book := OrderBookResponse{
|
||||
Asks: []orderbook.Level{
|
||||
{Price: 6621.80000000, Amount: 0.00198100},
|
||||
{Price: 6622.14000000, Amount: 4.00000000},
|
||||
{Price: 6622.46000000, Amount: 2.30000000},
|
||||
{Price: 6622.47000000, Amount: 1.18633300},
|
||||
{Price: 6622.64000000, Amount: 4.00000000},
|
||||
{Price: 6622.73000000, Amount: 0.02900000},
|
||||
{Price: 6622.76000000, Amount: 0.12557700},
|
||||
{Price: 6622.81000000, Amount: 2.08994200},
|
||||
{Price: 6622.82000000, Amount: 0.01500000},
|
||||
{Price: 6623.17000000, Amount: 0.16831300},
|
||||
},
|
||||
Bids: []OrderbookItem{
|
||||
{Price: 6621.55000000, Quantity: 0.16356700},
|
||||
{Price: 6621.45000000, Quantity: 0.16352600},
|
||||
{Price: 6621.41000000, Quantity: 0.86091200},
|
||||
{Price: 6621.25000000, Quantity: 0.16914100},
|
||||
{Price: 6621.23000000, Quantity: 0.09193600},
|
||||
{Price: 6621.22000000, Quantity: 0.00755100},
|
||||
{Price: 6621.13000000, Quantity: 0.08432000},
|
||||
{Price: 6621.03000000, Quantity: 0.00172000},
|
||||
{Price: 6620.94000000, Quantity: 0.30506700},
|
||||
{Price: 6620.93000000, Quantity: 0.00200000},
|
||||
Bids: []orderbook.Level{
|
||||
{Price: 6621.55000000, Amount: 0.16356700},
|
||||
{Price: 6621.45000000, Amount: 0.16352600},
|
||||
{Price: 6621.41000000, Amount: 0.86091200},
|
||||
{Price: 6621.25000000, Amount: 0.16914100},
|
||||
{Price: 6621.23000000, Amount: 0.09193600},
|
||||
{Price: 6621.22000000, Amount: 0.00755100},
|
||||
{Price: 6621.13000000, Amount: 0.08432000},
|
||||
{Price: 6621.03000000, Amount: 0.00172000},
|
||||
{Price: 6620.94000000, Amount: 0.30506700},
|
||||
{Price: 6620.93000000, Amount: 0.00200000},
|
||||
},
|
||||
LastUpdateID: seedLastUpdateID,
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/types"
|
||||
)
|
||||
|
||||
@@ -123,35 +124,14 @@ type CoinInfo struct {
|
||||
Withdrawing float64 `json:"withdrawing,string"`
|
||||
}
|
||||
|
||||
// OrderBookDataRequestParams represents Klines request data.
|
||||
type OrderBookDataRequestParams struct {
|
||||
Symbol currency.Pair `json:"symbol"` // Required field; example LTCBTC,BTCUSDT
|
||||
Limit int `json:"limit"` // Default 100; max 5000. If limit > 5000, then the response will truncate to 5000
|
||||
}
|
||||
|
||||
// OrderbookItem stores an individual orderbook item
|
||||
type OrderbookItem struct {
|
||||
Price float64
|
||||
Quantity float64
|
||||
}
|
||||
|
||||
// OrderBookData is resp data from orderbook endpoint
|
||||
type OrderBookData struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
LastUpdateID int64 `json:"lastUpdateId"`
|
||||
Bids [][2]types.Number `json:"bids"`
|
||||
Asks [][2]types.Number `json:"asks"`
|
||||
}
|
||||
|
||||
// OrderBook actual structured data that can be used for orderbook
|
||||
type OrderBook struct {
|
||||
Symbol string
|
||||
LastUpdateID int64
|
||||
Code int
|
||||
Msg string
|
||||
Bids []OrderbookItem
|
||||
Asks []OrderbookItem
|
||||
// OrderBookResponse is resp data from orderbook endpoint
|
||||
type OrderBookResponse struct {
|
||||
Code int64 `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
LastUpdateID int64 `json:"lastUpdateId"`
|
||||
Timestamp types.Time `json:"T"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"bids"`
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"asks"`
|
||||
}
|
||||
|
||||
// DepthUpdateParams is used as an embedded type for WebsocketDepthStream
|
||||
@@ -163,13 +143,13 @@ type DepthUpdateParams []struct {
|
||||
|
||||
// WebsocketDepthStream is the difference for the update depth stream
|
||||
type WebsocketDepthStream struct {
|
||||
Event string `json:"e"`
|
||||
Timestamp types.Time `json:"E"`
|
||||
Pair string `json:"s"`
|
||||
FirstUpdateID int64 `json:"U"`
|
||||
LastUpdateID int64 `json:"u"`
|
||||
UpdateBids [][2]types.Number `json:"b"`
|
||||
UpdateAsks [][2]types.Number `json:"a"`
|
||||
Event string `json:"e"`
|
||||
Timestamp types.Time `json:"E"`
|
||||
Pair string `json:"s"`
|
||||
FirstUpdateID int64 `json:"U"`
|
||||
LastUpdateID int64 `json:"u"`
|
||||
UpdateBids orderbook.LevelsArrayPriceAmount `json:"b"`
|
||||
UpdateAsks orderbook.LevelsArrayPriceAmount `json:"a"`
|
||||
}
|
||||
|
||||
// RecentTradeRequestParams represents Klines request data.
|
||||
|
||||
@@ -88,7 +88,7 @@ func (e *Exchange) UExchangeInfo(ctx context.Context) (UFuturesExchangeInfo, err
|
||||
}
|
||||
|
||||
// UFuturesOrderbook gets orderbook data for usdt margined futures
|
||||
func (e *Exchange) UFuturesOrderbook(ctx context.Context, symbol currency.Pair, limit int64) (*OrderBook, error) {
|
||||
func (e *Exchange) UFuturesOrderbook(ctx context.Context, symbol currency.Pair, limit int64) (*OrderBookResponse, error) {
|
||||
symbolValue, err := e.FormatSymbol(symbol, asset.USDTMarginedFutures)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -114,27 +114,8 @@ func (e *Exchange) UFuturesOrderbook(ctx context.Context, symbol currency.Pair,
|
||||
rateBudget = uFuturesOrderbook500Rate
|
||||
}
|
||||
|
||||
var data *OrderbookData
|
||||
if err := e.SendHTTPRequest(ctx, exchange.RestUSDTMargined, ufuturesOrderbook+params.Encode(), rateBudget, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob := &OrderBook{
|
||||
Symbol: symbolValue,
|
||||
LastUpdateID: data.LastUpdateID,
|
||||
Bids: make([]OrderbookItem, len(data.Bids)),
|
||||
Asks: make([]OrderbookItem, len(data.Asks)),
|
||||
}
|
||||
|
||||
for x := range data.Asks {
|
||||
ob.Asks[x].Price = data.Asks[x][0].Float64()
|
||||
ob.Asks[x].Quantity = data.Asks[x][1].Float64()
|
||||
}
|
||||
for x := range data.Bids {
|
||||
ob.Bids[x].Price = data.Bids[x][0].Float64()
|
||||
ob.Bids[x].Quantity = data.Bids[x][1].Float64()
|
||||
}
|
||||
return ob, nil
|
||||
var resp *OrderBookResponse
|
||||
return resp, e.SendHTTPRequest(ctx, exchange.RestUSDTMargined, ufuturesOrderbook+params.Encode(), rateBudget, &resp)
|
||||
}
|
||||
|
||||
// URecentTrades gets recent trades for usdt margined futures
|
||||
|
||||
@@ -449,11 +449,7 @@ func stringToOrderStatus(status string) (order.Status, error) {
|
||||
|
||||
// SeedLocalCache seeds depth data
|
||||
func (e *Exchange) SeedLocalCache(ctx context.Context, p currency.Pair) error {
|
||||
ob, err := e.GetOrderBook(ctx,
|
||||
OrderBookDataRequestParams{
|
||||
Symbol: p,
|
||||
Limit: 1000,
|
||||
})
|
||||
ob, err := e.GetOrderBook(ctx, p, 1000)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -461,28 +457,20 @@ func (e *Exchange) SeedLocalCache(ctx context.Context, p currency.Pair) error {
|
||||
}
|
||||
|
||||
// SeedLocalCacheWithBook seeds the local orderbook cache
|
||||
func (e *Exchange) SeedLocalCacheWithBook(p currency.Pair, orderbookNew *OrderBook) error {
|
||||
func (e *Exchange) SeedLocalCacheWithBook(p currency.Pair, orderbookNew *OrderBookResponse) error {
|
||||
t := orderbookNew.Timestamp.Time()
|
||||
if t.IsZero() {
|
||||
t = time.Now() // Time not provided for this REST book.
|
||||
}
|
||||
newOrderBook := orderbook.Book{
|
||||
Pair: p,
|
||||
Asset: asset.Spot,
|
||||
Exchange: e.Name,
|
||||
LastUpdateID: orderbookNew.LastUpdateID,
|
||||
ValidateOrderbook: e.ValidateOrderbook,
|
||||
Bids: make(orderbook.Levels, len(orderbookNew.Bids)),
|
||||
Asks: make(orderbook.Levels, len(orderbookNew.Asks)),
|
||||
LastUpdated: time.Now(), // Time not provided in REST book.
|
||||
}
|
||||
for i := range orderbookNew.Bids {
|
||||
newOrderBook.Bids[i] = orderbook.Level{
|
||||
Amount: orderbookNew.Bids[i].Quantity,
|
||||
Price: orderbookNew.Bids[i].Price,
|
||||
}
|
||||
}
|
||||
for i := range orderbookNew.Asks {
|
||||
newOrderBook.Asks[i] = orderbook.Level{
|
||||
Amount: orderbookNew.Asks[i].Quantity,
|
||||
Price: orderbookNew.Asks[i].Price,
|
||||
}
|
||||
Bids: orderbookNew.Bids.Levels(),
|
||||
Asks: orderbookNew.Asks.Levels(),
|
||||
LastUpdated: t,
|
||||
}
|
||||
return e.Websocket.Orderbook.LoadSnapshot(&newOrderBook)
|
||||
}
|
||||
@@ -618,23 +606,9 @@ func (e *Exchange) manageSubs(ctx context.Context, op string, subs subscription.
|
||||
|
||||
// ProcessOrderbookUpdate processes the websocket orderbook update
|
||||
func (e *Exchange) ProcessOrderbookUpdate(cp currency.Pair, a asset.Item, ws *WebsocketDepthStream) error {
|
||||
updateBid := make([]orderbook.Level, len(ws.UpdateBids))
|
||||
for i := range ws.UpdateBids {
|
||||
updateBid[i] = orderbook.Level{
|
||||
Price: ws.UpdateBids[i][0].Float64(),
|
||||
Amount: ws.UpdateBids[i][1].Float64(),
|
||||
}
|
||||
}
|
||||
updateAsk := make([]orderbook.Level, len(ws.UpdateAsks))
|
||||
for i := range ws.UpdateAsks {
|
||||
updateAsk[i] = orderbook.Level{
|
||||
Price: ws.UpdateAsks[i][0].Float64(),
|
||||
Amount: ws.UpdateAsks[i][1].Float64(),
|
||||
}
|
||||
}
|
||||
return e.Websocket.Orderbook.Update(&orderbook.Update{
|
||||
Bids: updateBid,
|
||||
Asks: updateAsk,
|
||||
Bids: ws.UpdateBids.Levels(),
|
||||
Asks: ws.UpdateAsks.Levels(),
|
||||
Pair: cp,
|
||||
UpdateID: ws.LastUpdateID,
|
||||
UpdateTime: ws.Timestamp.Time(),
|
||||
|
||||
@@ -512,60 +512,44 @@ func (e *Exchange) UpdateTicker(ctx context.Context, p currency.Pair, a asset.It
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (e *Exchange) UpdateOrderbook(ctx context.Context, p currency.Pair, assetType asset.Item) (*orderbook.Book, error) {
|
||||
func (e *Exchange) UpdateOrderbook(ctx context.Context, p currency.Pair, a asset.Item) (*orderbook.Book, error) {
|
||||
if p.IsEmpty() {
|
||||
return nil, currency.ErrCurrencyPairEmpty
|
||||
}
|
||||
if err := e.CurrencyPairs.IsAssetEnabled(assetType); err != nil {
|
||||
if err := e.CurrencyPairs.IsAssetEnabled(a); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
book := &orderbook.Book{
|
||||
Exchange: e.Name,
|
||||
Pair: p,
|
||||
Asset: assetType,
|
||||
ValidateOrderbook: e.ValidateOrderbook,
|
||||
}
|
||||
var orderbookNew *OrderBook
|
||||
var err error
|
||||
|
||||
switch assetType {
|
||||
var orderbookNew *OrderBookResponse
|
||||
var err error
|
||||
switch a {
|
||||
case asset.Spot, asset.Margin:
|
||||
orderbookNew, err = e.GetOrderBook(ctx,
|
||||
OrderBookDataRequestParams{
|
||||
Symbol: p,
|
||||
Limit: 1000,
|
||||
})
|
||||
orderbookNew, err = e.GetOrderBook(ctx, p, 1000)
|
||||
case asset.USDTMarginedFutures:
|
||||
orderbookNew, err = e.UFuturesOrderbook(ctx, p, 1000)
|
||||
case asset.CoinMarginedFutures:
|
||||
orderbookNew, err = e.GetFuturesOrderbook(ctx, p, 1000)
|
||||
default:
|
||||
return nil, fmt.Errorf("[%s] %w", assetType, asset.ErrNotSupported)
|
||||
return nil, fmt.Errorf("[%s] %w", a, asset.ErrNotSupported)
|
||||
}
|
||||
if err != nil {
|
||||
return book, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
book.Bids = make(orderbook.Levels, len(orderbookNew.Bids))
|
||||
for x := range orderbookNew.Bids {
|
||||
book.Bids[x] = orderbook.Level{
|
||||
Amount: orderbookNew.Bids[x].Quantity,
|
||||
Price: orderbookNew.Bids[x].Price,
|
||||
}
|
||||
}
|
||||
book.Asks = make(orderbook.Levels, len(orderbookNew.Asks))
|
||||
for x := range orderbookNew.Asks {
|
||||
book.Asks[x] = orderbook.Level{
|
||||
Amount: orderbookNew.Asks[x].Quantity,
|
||||
Price: orderbookNew.Asks[x].Price,
|
||||
}
|
||||
ob := &orderbook.Book{
|
||||
Exchange: e.Name,
|
||||
Pair: p,
|
||||
Asset: a,
|
||||
ValidateOrderbook: e.ValidateOrderbook,
|
||||
Bids: orderbookNew.Bids.Levels(),
|
||||
Asks: orderbookNew.Asks.Levels(),
|
||||
}
|
||||
|
||||
err = book.Process()
|
||||
if err != nil {
|
||||
return book, err
|
||||
if err := ob.Process(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return orderbook.Get(e.Name, p, assetType)
|
||||
|
||||
return orderbook.Get(e.Name, p, a)
|
||||
}
|
||||
|
||||
// UpdateAccountInfo retrieves balances for all enabled currencies for the
|
||||
|
||||
@@ -181,7 +181,7 @@ func openOrdersLimit(symbol string) request.EndpointLimit {
|
||||
return spotOpenOrdersSpecificRate
|
||||
}
|
||||
|
||||
func orderbookLimit(depth int) request.EndpointLimit {
|
||||
func orderbookLimit(depth uint64) request.EndpointLimit {
|
||||
switch {
|
||||
case depth <= 100:
|
||||
return spotOrderbookDepth100Rate
|
||||
|
||||
@@ -38,16 +38,6 @@ var (
|
||||
uValidPeriods = []string{"5m", "15m", "30m", "1h", "2h", "4h", "6h", "12h", "1d"}
|
||||
)
|
||||
|
||||
// USDT Margined Futures
|
||||
|
||||
// OrderbookData stores ob data for umargined and cmargined futures
|
||||
type OrderbookData struct {
|
||||
LastUpdateID int64 `json:"lastUpdateID"`
|
||||
Timestamp types.Time `json:"T"`
|
||||
Bids [][2]types.Number `json:"bids"`
|
||||
Asks [][2]types.Number `json:"asks"`
|
||||
}
|
||||
|
||||
// UPublicTradesData stores trade data
|
||||
type UPublicTradesData struct {
|
||||
ID int64 `json:"id"`
|
||||
|
||||
@@ -318,34 +318,25 @@ outer:
|
||||
}
|
||||
|
||||
// GetOrderBookDepth to get the order book depth. Please note the limits in the table below.
|
||||
func (e *Exchange) GetOrderBookDepth(ctx context.Context, arg *OrderBookDataRequestParams) (*OrderBook, error) {
|
||||
func (e *Exchange) GetOrderBookDepth(ctx context.Context, pair currency.Pair, limit uint64) (*OrderBook, error) {
|
||||
params := url.Values{}
|
||||
symbol, err := e.FormatSymbol(arg.Symbol, asset.Spot)
|
||||
symbol, err := e.FormatSymbol(pair, asset.Spot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.Set("symbol", symbol)
|
||||
params.Set("limit", strconv.FormatInt(arg.Limit, 10))
|
||||
params.Set("limit", strconv.FormatUint(limit, 10))
|
||||
|
||||
var resp *OrderBookData
|
||||
if err := e.SendHTTPRequest(ctx, exchange.RestSpotSupplementary, common.EncodeURLValues(orderBookDepth, params), orderbookLimit(arg.Limit), &resp); err != nil {
|
||||
if err := e.SendHTTPRequest(ctx, exchange.RestSpotSupplementary, common.EncodeURLValues(orderBookDepth, params), orderbookLimit(limit), &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob := &OrderBook{
|
||||
Bids: make([]OrderbookItem, len(resp.Bids)),
|
||||
Asks: make([]OrderbookItem, len(resp.Asks)),
|
||||
return &OrderBook{
|
||||
Bids: resp.Bids.Levels(),
|
||||
Asks: resp.Asks.Levels(),
|
||||
LastUpdateID: resp.LastUpdateID,
|
||||
}
|
||||
for x := range resp.Bids {
|
||||
ob.Bids[x].Price = resp.Bids[x][0].Float64()
|
||||
ob.Bids[x].Quantity = resp.Bids[x][1].Float64()
|
||||
}
|
||||
for x := range resp.Asks {
|
||||
ob.Asks[x].Price = resp.Asks[x][0].Float64()
|
||||
ob.Asks[x].Quantity = resp.Asks[x][1].Float64()
|
||||
}
|
||||
return ob, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetIntervalEnum allowed interval params by Binanceus
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
|
||||
testexch "github.com/thrasher-corp/gocryptotrader/internal/testing/exchange"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
|
||||
@@ -448,13 +449,8 @@ func TestGetAggregateTrades(t *testing.T) {
|
||||
|
||||
func TestGetOrderBookDepth(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, er := e.GetOrderBookDepth(t.Context(), &OrderBookDataRequestParams{
|
||||
Symbol: currency.NewBTCUSDT(),
|
||||
Limit: 1000,
|
||||
})
|
||||
if er != nil {
|
||||
t.Error("Binanceus GetOrderBook() error", er)
|
||||
}
|
||||
_, err := e.GetOrderBookDepth(t.Context(), currency.NewBTCUSDT(), 1000)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGetCandlestickData(t *testing.T) {
|
||||
@@ -1338,29 +1334,29 @@ func TestWebsocketOrderBookDepthDiffStream(t *testing.T) {
|
||||
e.setupOrderbookManager(t.Context())
|
||||
seedLastUpdateID := int64(161)
|
||||
book := OrderBook{
|
||||
Asks: []OrderbookItem{
|
||||
{Price: 6621.80000000, Quantity: 0.00198100},
|
||||
{Price: 6622.14000000, Quantity: 4.00000000},
|
||||
{Price: 6622.46000000, Quantity: 2.30000000},
|
||||
{Price: 6622.47000000, Quantity: 1.18633300},
|
||||
{Price: 6622.64000000, Quantity: 4.00000000},
|
||||
{Price: 6622.73000000, Quantity: 0.02900000},
|
||||
{Price: 6622.76000000, Quantity: 0.12557700},
|
||||
{Price: 6622.81000000, Quantity: 2.08994200},
|
||||
{Price: 6622.82000000, Quantity: 0.01500000},
|
||||
{Price: 6623.17000000, Quantity: 0.16831300},
|
||||
Asks: []orderbook.Level{
|
||||
{Price: 6621.80000000, Amount: 0.00198100},
|
||||
{Price: 6622.14000000, Amount: 4.00000000},
|
||||
{Price: 6622.46000000, Amount: 2.30000000},
|
||||
{Price: 6622.47000000, Amount: 1.18633300},
|
||||
{Price: 6622.64000000, Amount: 4.00000000},
|
||||
{Price: 6622.73000000, Amount: 0.02900000},
|
||||
{Price: 6622.76000000, Amount: 0.12557700},
|
||||
{Price: 6622.81000000, Amount: 2.08994200},
|
||||
{Price: 6622.82000000, Amount: 0.01500000},
|
||||
{Price: 6623.17000000, Amount: 0.16831300},
|
||||
},
|
||||
Bids: []OrderbookItem{
|
||||
{Price: 6621.55000000, Quantity: 0.16356700},
|
||||
{Price: 6621.45000000, Quantity: 0.16352600},
|
||||
{Price: 6621.41000000, Quantity: 0.86091200},
|
||||
{Price: 6621.25000000, Quantity: 0.16914100},
|
||||
{Price: 6621.23000000, Quantity: 0.09193600},
|
||||
{Price: 6621.22000000, Quantity: 0.00755100},
|
||||
{Price: 6621.13000000, Quantity: 0.08432000},
|
||||
{Price: 6621.03000000, Quantity: 0.00172000},
|
||||
{Price: 6620.94000000, Quantity: 0.30506700},
|
||||
{Price: 6620.93000000, Quantity: 0.00200000},
|
||||
Bids: []orderbook.Level{
|
||||
{Price: 6621.55000000, Amount: 0.16356700},
|
||||
{Price: 6621.45000000, Amount: 0.16352600},
|
||||
{Price: 6621.41000000, Amount: 0.86091200},
|
||||
{Price: 6621.25000000, Amount: 0.16914100},
|
||||
{Price: 6621.23000000, Amount: 0.09193600},
|
||||
{Price: 6621.22000000, Amount: 0.00755100},
|
||||
{Price: 6621.13000000, Amount: 0.08432000},
|
||||
{Price: 6621.03000000, Amount: 0.00172000},
|
||||
{Price: 6620.94000000, Amount: 0.30506700},
|
||||
{Price: 6620.93000000, Amount: 0.00200000},
|
||||
},
|
||||
LastUpdateID: seedLastUpdateID,
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/trade"
|
||||
"github.com/thrasher-corp/gocryptotrader/types"
|
||||
)
|
||||
@@ -161,23 +162,11 @@ func (a *AggregatedTrade) toTradeData(p currency.Pair, exchange string, aType as
|
||||
}
|
||||
}
|
||||
|
||||
// OrderBookDataRequestParams represents Klines request data.
|
||||
type OrderBookDataRequestParams struct {
|
||||
Symbol currency.Pair `json:"symbol"` // Required field; example LTCBTC,BTCUSDT
|
||||
Limit int64 `json:"limit"` // Default 100; max 5000. If limit > 5000, then the response will truncate to 5000
|
||||
}
|
||||
|
||||
// OrderbookItem stores an individual orderbook item
|
||||
type OrderbookItem struct {
|
||||
Price float64
|
||||
Quantity float64
|
||||
}
|
||||
|
||||
// OrderBookData is resp data from orderbook endpoint
|
||||
type OrderBookData struct {
|
||||
LastUpdateID int64 `json:"lastUpdateId"`
|
||||
Bids [][2]types.Number `json:"bids"`
|
||||
Asks [][2]types.Number `json:"asks"`
|
||||
LastUpdateID int64 `json:"lastUpdateId"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"bids"`
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"asks"`
|
||||
}
|
||||
|
||||
// OrderBook actual structured data that can be used for orderbook
|
||||
@@ -186,8 +175,8 @@ type OrderBook struct {
|
||||
LastUpdateID int64
|
||||
Code int
|
||||
Msg string
|
||||
Bids []OrderbookItem
|
||||
Asks []OrderbookItem
|
||||
Bids []orderbook.Level
|
||||
Asks []orderbook.Level
|
||||
}
|
||||
|
||||
// KlinesRequestParams represents Klines request data.
|
||||
@@ -833,20 +822,20 @@ type update struct {
|
||||
|
||||
// WebsocketDepthStream is the difference for the update depth stream
|
||||
type WebsocketDepthStream struct {
|
||||
Event string `json:"e"`
|
||||
Timestamp types.Time `json:"E"`
|
||||
Pair string `json:"s"`
|
||||
FirstUpdateID int64 `json:"U"`
|
||||
LastUpdateID int64 `json:"u"`
|
||||
UpdateBids [][2]types.Number `json:"b"`
|
||||
UpdateAsks [][2]types.Number `json:"a"`
|
||||
Event string `json:"e"`
|
||||
Timestamp types.Time `json:"E"`
|
||||
Pair string `json:"s"`
|
||||
FirstUpdateID int64 `json:"U"`
|
||||
LastUpdateID int64 `json:"u"`
|
||||
UpdateBids orderbook.LevelsArrayPriceAmount `json:"b"`
|
||||
UpdateAsks orderbook.LevelsArrayPriceAmount `json:"a"`
|
||||
}
|
||||
|
||||
// WebsocketDepthDiffStream websocket response of depth diff stream
|
||||
type WebsocketDepthDiffStream struct {
|
||||
LastUpdateID int64 `json:"lastUpdateId"`
|
||||
Bids [][2]types.Number `json:"bids"`
|
||||
Asks [][2]types.Number `json:"asks"`
|
||||
LastUpdateID int64 `json:"lastUpdateId"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"bids"`
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"asks"`
|
||||
}
|
||||
|
||||
// WsAccountInfoData defines websocket account info data
|
||||
|
||||
@@ -654,21 +654,9 @@ func (e *Exchange) SynchroniseWebsocketOrderbook(ctx context.Context) {
|
||||
|
||||
// ProcessOrderbookUpdate processes the websocket orderbook update
|
||||
func (e *Exchange) ProcessOrderbookUpdate(cp currency.Pair, a asset.Item, wsDSUpdate *WebsocketDepthStream) error {
|
||||
updateBid := make([]orderbook.Level, len(wsDSUpdate.UpdateBids))
|
||||
for i := range wsDSUpdate.UpdateBids {
|
||||
updateBid[i].Price = wsDSUpdate.UpdateBids[i][0].Float64()
|
||||
updateBid[i].Amount = wsDSUpdate.UpdateBids[i][1].Float64()
|
||||
}
|
||||
|
||||
updateAsk := make([]orderbook.Level, len(wsDSUpdate.UpdateAsks))
|
||||
for i := range wsDSUpdate.UpdateAsks {
|
||||
updateAsk[i].Price = wsDSUpdate.UpdateAsks[i][0].Float64()
|
||||
updateAsk[i].Amount = wsDSUpdate.UpdateAsks[i][1].Float64()
|
||||
}
|
||||
|
||||
return e.Websocket.Orderbook.Update(&orderbook.Update{
|
||||
Bids: updateBid,
|
||||
Asks: updateAsk,
|
||||
Bids: wsDSUpdate.UpdateBids.Levels(),
|
||||
Asks: wsDSUpdate.UpdateAsks.Levels(),
|
||||
Pair: cp,
|
||||
UpdateID: wsDSUpdate.LastUpdateID,
|
||||
UpdateTime: wsDSUpdate.Timestamp.Time(),
|
||||
@@ -789,11 +777,7 @@ func (e *Exchange) processJob(ctx context.Context, p currency.Pair) error {
|
||||
|
||||
// SeedLocalCache seeds depth data
|
||||
func (e *Exchange) SeedLocalCache(ctx context.Context, p currency.Pair) error {
|
||||
ob, err := e.GetOrderBookDepth(ctx,
|
||||
&OrderBookDataRequestParams{
|
||||
Symbol: p,
|
||||
Limit: 1000,
|
||||
})
|
||||
ob, err := e.GetOrderBookDepth(ctx, p, 1000)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -802,29 +786,16 @@ func (e *Exchange) SeedLocalCache(ctx context.Context, p currency.Pair) error {
|
||||
|
||||
// SeedLocalCacheWithBook seeds the local orderbook cache
|
||||
func (e *Exchange) SeedLocalCacheWithBook(p currency.Pair, orderbookNew *OrderBook) error {
|
||||
newOrderBook := orderbook.Book{
|
||||
return e.Websocket.Orderbook.LoadSnapshot(&orderbook.Book{
|
||||
Pair: p,
|
||||
Asset: asset.Spot,
|
||||
Exchange: e.Name,
|
||||
LastUpdateID: orderbookNew.LastUpdateID,
|
||||
ValidateOrderbook: e.ValidateOrderbook,
|
||||
Bids: make(orderbook.Levels, len(orderbookNew.Bids)),
|
||||
Asks: make(orderbook.Levels, len(orderbookNew.Asks)),
|
||||
Bids: orderbookNew.Bids,
|
||||
Asks: orderbookNew.Asks,
|
||||
LastUpdated: time.Now(), // Time not provided in REST book.
|
||||
}
|
||||
for i := range orderbookNew.Bids {
|
||||
newOrderBook.Bids[i] = orderbook.Level{
|
||||
Amount: orderbookNew.Bids[i].Quantity,
|
||||
Price: orderbookNew.Bids[i].Price,
|
||||
}
|
||||
}
|
||||
for i := range orderbookNew.Asks {
|
||||
newOrderBook.Asks[i] = orderbook.Level{
|
||||
Amount: orderbookNew.Asks[i].Quantity,
|
||||
Price: orderbookNew.Asks[i].Price,
|
||||
}
|
||||
}
|
||||
return e.Websocket.Orderbook.LoadSnapshot(&newOrderBook)
|
||||
})
|
||||
}
|
||||
|
||||
// handleFetchingBook checks if a full book is being fetched or needs to be
|
||||
|
||||
@@ -317,37 +317,20 @@ func (e *Exchange) UpdateOrderbook(ctx context.Context, pair currency.Pair, asse
|
||||
if err := e.CurrencyPairs.IsAssetEnabled(assetType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
book := &orderbook.Book{
|
||||
orderbookNew, err := e.GetOrderBookDepth(ctx, pair, 1000)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ob := &orderbook.Book{
|
||||
Exchange: e.Name,
|
||||
Pair: pair,
|
||||
Asset: assetType,
|
||||
ValidateOrderbook: e.ValidateOrderbook,
|
||||
Bids: orderbookNew.Bids,
|
||||
Asks: orderbookNew.Asks,
|
||||
}
|
||||
|
||||
orderbookNew, err := e.GetOrderBookDepth(ctx, &OrderBookDataRequestParams{
|
||||
Symbol: pair,
|
||||
Limit: 1000,
|
||||
})
|
||||
if err != nil {
|
||||
return book, err
|
||||
}
|
||||
book.Bids = make([]orderbook.Level, len(orderbookNew.Bids))
|
||||
for x := range orderbookNew.Bids {
|
||||
book.Bids[x] = orderbook.Level{
|
||||
Amount: orderbookNew.Bids[x].Quantity,
|
||||
Price: orderbookNew.Bids[x].Price,
|
||||
}
|
||||
}
|
||||
book.Asks = make([]orderbook.Level, len(orderbookNew.Asks))
|
||||
for x := range orderbookNew.Asks {
|
||||
book.Asks[x] = orderbook.Level{
|
||||
Amount: orderbookNew.Asks[x].Quantity,
|
||||
Price: orderbookNew.Asks[x].Price,
|
||||
}
|
||||
}
|
||||
err = book.Process()
|
||||
if err != nil {
|
||||
return book, err
|
||||
if err := ob.Process(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return orderbook.Get(e.Name, pair, assetType)
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ func GetRateLimit() request.RateLimitDefinitions {
|
||||
}
|
||||
|
||||
// orderbookLimit returns the endpoint rate limit representing enum given order depth
|
||||
func orderbookLimit(depth int64) request.EndpointLimit {
|
||||
func orderbookLimit(depth uint64) request.EndpointLimit {
|
||||
switch {
|
||||
case depth <= 100:
|
||||
return spotDefaultRate
|
||||
|
||||
@@ -170,35 +170,18 @@ func (e *Exchange) GetTicker(ctx context.Context, symbol string, hourly bool) (*
|
||||
// the amount.
|
||||
func (e *Exchange) GetOrderbook(ctx context.Context, symbol string) (*Orderbook, error) {
|
||||
type response struct {
|
||||
Timestamp types.Time `json:"timestamp"`
|
||||
Bids [][2]types.Number `json:"bids"`
|
||||
Asks [][2]types.Number `json:"asks"`
|
||||
Timestamp types.Time `json:"timestamp"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"bids"`
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"asks"`
|
||||
}
|
||||
|
||||
path := "/v" + bitstampAPIVersion + "/" + bitstampAPIOrderbook + "/" + strings.ToLower(symbol) + "/"
|
||||
var resp response
|
||||
err := e.SendHTTPRequest(ctx, exchange.RestSpot, path, &resp)
|
||||
if err != nil {
|
||||
if err := e.SendHTTPRequest(ctx, exchange.RestSpot, path, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob := &Orderbook{
|
||||
Timestamp: resp.Timestamp.Time(),
|
||||
Bids: make([]OrderbookBase, len(resp.Bids)),
|
||||
Asks: make([]OrderbookBase, len(resp.Asks)),
|
||||
}
|
||||
|
||||
for x := range resp.Bids {
|
||||
ob.Bids[x].Price = resp.Bids[x][0].Float64()
|
||||
ob.Bids[x].Amount = resp.Bids[x][1].Float64()
|
||||
}
|
||||
|
||||
for x := range resp.Asks {
|
||||
ob.Asks[x].Price = resp.Asks[x][0].Float64()
|
||||
ob.Asks[x].Amount = resp.Asks[x][1].Float64()
|
||||
}
|
||||
|
||||
return ob, nil
|
||||
return &Orderbook{Timestamp: resp.Timestamp.Time(), Bids: resp.Bids.Levels(), Asks: resp.Asks.Levels()}, nil
|
||||
}
|
||||
|
||||
// GetTradingPairs returns a list of trading pairs which Bitstamp
|
||||
|
||||
@@ -197,7 +197,7 @@ func TestGetOrderbook(t *testing.T) {
|
||||
ob, err := e.GetOrderbook(t.Context(), currency.BTC.String()+currency.USD.String())
|
||||
require.NoError(t, err, "GetOrderbook must not error")
|
||||
assert.NotEmpty(t, ob.Timestamp, "Timestamp should not be empty")
|
||||
for i, o := range [][]OrderbookBase{ob.Asks, ob.Bids} {
|
||||
for i, o := range [][]orderbook.Level{ob.Asks, ob.Bids} {
|
||||
s := []string{"Ask", "Bid"}[i]
|
||||
if assert.NotEmptyf(t, o, "Should have items in %ss", s) {
|
||||
a := o[0]
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/types"
|
||||
)
|
||||
|
||||
@@ -37,17 +38,11 @@ type Ticker struct {
|
||||
PercentChange24 float64 `json:"percent_change_24,string"`
|
||||
}
|
||||
|
||||
// OrderbookBase holds singular price information
|
||||
type OrderbookBase struct {
|
||||
Price float64
|
||||
Amount float64
|
||||
}
|
||||
|
||||
// Orderbook holds orderbook information
|
||||
type Orderbook struct {
|
||||
Timestamp time.Time
|
||||
Bids []OrderbookBase
|
||||
Asks []OrderbookBase
|
||||
Bids []orderbook.Level
|
||||
Asks []orderbook.Level
|
||||
}
|
||||
|
||||
// TradingPair holds trading pair information
|
||||
@@ -276,10 +271,10 @@ type websocketOrderBookResponse struct {
|
||||
}
|
||||
|
||||
type websocketOrderBook struct {
|
||||
Asks [][2]types.Number `json:"asks"`
|
||||
Bids [][2]types.Number `json:"bids"`
|
||||
Timestamp types.Time `json:"timestamp"`
|
||||
Microtimestamp types.Time `json:"microtimestamp"`
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"asks"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"bids"`
|
||||
Timestamp types.Time `json:"timestamp"`
|
||||
Microtimestamp types.Time `json:"microtimestamp"`
|
||||
}
|
||||
|
||||
// OHLCResponse holds returned candle data
|
||||
|
||||
@@ -300,23 +300,14 @@ func (e *Exchange) handleWSOrderbook(msg []byte) error {
|
||||
}
|
||||
|
||||
obUpdate := &orderbook.Book{
|
||||
Bids: make(orderbook.Levels, len(wsOrderBookResp.Data.Bids)),
|
||||
Asks: make(orderbook.Levels, len(wsOrderBookResp.Data.Asks)),
|
||||
Bids: wsOrderBookResp.Data.Bids.Levels(),
|
||||
Asks: wsOrderBookResp.Data.Asks.Levels(),
|
||||
Pair: p,
|
||||
LastUpdated: wsOrderBookResp.Data.Microtimestamp.Time(),
|
||||
Asset: asset.Spot,
|
||||
Exchange: e.Name,
|
||||
ValidateOrderbook: e.ValidateOrderbook,
|
||||
}
|
||||
|
||||
for i := range wsOrderBookResp.Data.Asks {
|
||||
obUpdate.Asks[i].Price = wsOrderBookResp.Data.Asks[i][0].Float64()
|
||||
obUpdate.Asks[i].Amount = wsOrderBookResp.Data.Asks[i][1].Float64()
|
||||
}
|
||||
for i := range wsOrderBookResp.Data.Bids {
|
||||
obUpdate.Bids[i].Price = wsOrderBookResp.Data.Bids[i][0].Float64()
|
||||
obUpdate.Bids[i].Amount = wsOrderBookResp.Data.Bids[i][1].Float64()
|
||||
}
|
||||
filterOrderbookZeroBidPrice(obUpdate)
|
||||
return e.Websocket.Orderbook.LoadSnapshot(obUpdate)
|
||||
}
|
||||
|
||||
@@ -148,22 +148,7 @@ func (e *Exchange) GetOrderbook(ctx context.Context, marketID string, level int6
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob := &Orderbook{
|
||||
MarketID: resp.MarketID,
|
||||
SnapshotID: resp.SnapshotID,
|
||||
Bids: make([]OBData, len(resp.Bids)),
|
||||
Asks: make([]OBData, len(resp.Asks)),
|
||||
}
|
||||
|
||||
for x := range resp.Asks {
|
||||
ob.Asks[x].Price = resp.Asks[x][0].Float64()
|
||||
ob.Asks[x].Volume = resp.Asks[x][1].Float64()
|
||||
}
|
||||
for x := range resp.Bids {
|
||||
ob.Bids[x].Price = resp.Bids[x][0].Float64()
|
||||
ob.Bids[x].Volume = resp.Bids[x][1].Float64()
|
||||
}
|
||||
return ob, nil
|
||||
return &Orderbook{MarketID: resp.MarketID, SnapshotID: resp.SnapshotID, Bids: resp.Bids.Levels(), Asks: resp.Asks.Levels()}, nil
|
||||
}
|
||||
|
||||
// GetMarketCandles gets candles for specified currency pair
|
||||
@@ -227,16 +212,8 @@ func (e *Exchange) GetMultipleOrderbooks(ctx context.Context, marketIDs []string
|
||||
orderbooks[i] = Orderbook{
|
||||
MarketID: resp[i].MarketID,
|
||||
SnapshotID: resp[i].SnapshotID,
|
||||
Asks: make([]OBData, len(resp[i].Asks)),
|
||||
Bids: make([]OBData, len(resp[i].Bids)),
|
||||
}
|
||||
for j := range resp[i].Asks {
|
||||
orderbooks[i].Asks[j].Price = resp[i].Asks[j][0].Float64()
|
||||
orderbooks[i].Asks[j].Volume = resp[i].Asks[j][1].Float64()
|
||||
}
|
||||
for j := range resp[i].Bids {
|
||||
orderbooks[i].Bids[j].Price = resp[i].Bids[j][0].Float64()
|
||||
orderbooks[i].Bids[j].Volume = resp[i].Bids[j][1].Float64()
|
||||
Asks: resp[i].Asks.Levels(),
|
||||
Bids: resp[i].Bids.Levels(),
|
||||
}
|
||||
}
|
||||
return orderbooks, nil
|
||||
|
||||
@@ -46,24 +46,18 @@ type Trade struct {
|
||||
|
||||
// tempOrderbook stores orderbook data
|
||||
type tempOrderbook struct {
|
||||
MarketID currency.Pair `json:"marketId"`
|
||||
SnapshotID int64 `json:"snapshotId"`
|
||||
Asks [][2]types.Number `json:"asks"`
|
||||
Bids [][2]types.Number `json:"bids"`
|
||||
}
|
||||
|
||||
// OBData stores orderbook data
|
||||
type OBData struct {
|
||||
Price float64
|
||||
Volume float64
|
||||
MarketID currency.Pair `json:"marketId"`
|
||||
SnapshotID int64 `json:"snapshotId"`
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"asks"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"bids"`
|
||||
}
|
||||
|
||||
// Orderbook holds current orderbook information returned from the exchange
|
||||
type Orderbook struct {
|
||||
MarketID currency.Pair
|
||||
SnapshotID int64
|
||||
Asks []OBData
|
||||
Bids []OBData
|
||||
Asks []orderbook.Level
|
||||
Bids []orderbook.Level
|
||||
}
|
||||
|
||||
// MarketCandle stores candle data for a given pair
|
||||
|
||||
@@ -257,43 +257,28 @@ func (e *Exchange) UpdateOrderbook(ctx context.Context, p currency.Pair, assetTy
|
||||
return nil, err
|
||||
}
|
||||
|
||||
book := &orderbook.Book{
|
||||
fPair, err := e.FormatExchangeCurrency(p, assetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Retrieve level one book which is the top 50 ask and bids, this is not
|
||||
// cached.
|
||||
resp, err := e.GetOrderbook(ctx, fPair.String(), 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ob := &orderbook.Book{
|
||||
Exchange: e.Name,
|
||||
Pair: p,
|
||||
Asset: assetType,
|
||||
PriceDuplication: true,
|
||||
ValidateOrderbook: e.ValidateOrderbook,
|
||||
Asks: resp.Asks,
|
||||
Bids: resp.Bids,
|
||||
}
|
||||
|
||||
fPair, err := e.FormatExchangeCurrency(p, assetType)
|
||||
if err != nil {
|
||||
return book, err
|
||||
}
|
||||
// Retrieve level one book which is the top 50 ask and bids, this is not
|
||||
// cached.
|
||||
tempResp, err := e.GetOrderbook(ctx, fPair.String(), 1)
|
||||
if err != nil {
|
||||
return book, err
|
||||
}
|
||||
|
||||
book.Bids = make(orderbook.Levels, len(tempResp.Bids))
|
||||
for x := range tempResp.Bids {
|
||||
book.Bids[x] = orderbook.Level{
|
||||
Amount: tempResp.Bids[x].Volume,
|
||||
Price: tempResp.Bids[x].Price,
|
||||
}
|
||||
}
|
||||
|
||||
book.Asks = make(orderbook.Levels, len(tempResp.Asks))
|
||||
for y := range tempResp.Asks {
|
||||
book.Asks[y] = orderbook.Level{
|
||||
Amount: tempResp.Asks[y].Volume,
|
||||
Price: tempResp.Asks[y].Price,
|
||||
}
|
||||
}
|
||||
err = book.Process()
|
||||
if err != nil {
|
||||
return book, err
|
||||
if err := ob.Process(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return orderbook.Get(e.Name, p, assetType)
|
||||
}
|
||||
|
||||
@@ -22,9 +22,7 @@ import (
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/types"
|
||||
)
|
||||
|
||||
// Exchange implements exchange.IBotExchange and contains additional specific api methods for interacting with Bybit
|
||||
@@ -247,29 +245,18 @@ func (e *Exchange) GetOrderBook(ctx context.Context, category, symbol string, li
|
||||
params.Set("limit", strconv.FormatInt(limit, 10))
|
||||
}
|
||||
var resp orderbookResponse
|
||||
err = e.SendHTTPRequest(ctx, exchange.RestSpot, common.EncodeURLValues("market/orderbook", params), defaultEPL, &resp)
|
||||
if err != nil {
|
||||
if err := e.SendHTTPRequest(ctx, exchange.RestSpot, common.EncodeURLValues("market/orderbook", params), defaultEPL, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Orderbook{
|
||||
Symbol: resp.Symbol,
|
||||
UpdateID: resp.UpdateID,
|
||||
Symbol: resp.Symbol,
|
||||
GenerationTime: resp.Timestamp.Time(),
|
||||
Bids: processOB(resp.Bids),
|
||||
Asks: processOB(resp.Asks),
|
||||
Bids: resp.Bids.Levels(),
|
||||
Asks: resp.Asks.Levels(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func processOB(ob [][2]types.Number) []orderbook.Level {
|
||||
o := make([]orderbook.Level, len(ob))
|
||||
for x := range ob {
|
||||
o[x].Price = ob[x][0].Float64()
|
||||
o[x].Amount = ob[x][1].Float64()
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
func fillCategoryAndSymbol(category, symbol string, optionalSymbol ...bool) (url.Values, error) {
|
||||
if category == "" {
|
||||
return nil, errCategoryNotSet
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package bybit
|
||||
|
||||
import "github.com/thrasher-corp/gocryptotrader/encoding/json"
|
||||
|
||||
// UnmarshalJSON deserializes incoming data into orderbookResponse instance.
|
||||
func (a *orderbookResponse) UnmarshalJSON(data []byte) error {
|
||||
type Alias orderbookResponse
|
||||
child := &struct {
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(a),
|
||||
}
|
||||
err := json.Unmarshal(data, child)
|
||||
if err != nil {
|
||||
var resp []any
|
||||
err = json.Unmarshal(data, &resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -47,7 +47,6 @@ const (
|
||||
canManipulateRealOrders = false
|
||||
|
||||
skipAuthenticatedFunctionsForMockTesting = "skipping authenticated function for mock testing"
|
||||
skippingWebsocketFunctionsForMockTesting = "skipping websocket function for mock testing"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -16,11 +16,11 @@ import (
|
||||
var supportedOptionsTypes = []string{"BTC", "ETH", "SOL"}
|
||||
|
||||
type orderbookResponse struct {
|
||||
Symbol string `json:"s"`
|
||||
Asks [][2]types.Number `json:"a"`
|
||||
Bids [][2]types.Number `json:"b"`
|
||||
Timestamp types.Time `json:"ts"`
|
||||
UpdateID int64 `json:"u"`
|
||||
Symbol string `json:"s"`
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"a"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"b"`
|
||||
Timestamp types.Time `json:"ts"`
|
||||
UpdateID int64 `json:"u"`
|
||||
}
|
||||
|
||||
// Authenticate stores authentication variables required
|
||||
@@ -1749,11 +1749,11 @@ type Orderbook struct {
|
||||
|
||||
// WsOrderbookDetail represents an orderbook detail information.
|
||||
type WsOrderbookDetail struct {
|
||||
Symbol string `json:"s"`
|
||||
Bids [][2]types.Number `json:"b"`
|
||||
Asks [][2]types.Number `json:"a"`
|
||||
UpdateID int64 `json:"u"`
|
||||
Sequence int64 `json:"seq"`
|
||||
Symbol string `json:"s"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"b"`
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"a"`
|
||||
UpdateID int64 `json:"u"`
|
||||
Sequence int64 `json:"seq"`
|
||||
}
|
||||
|
||||
// SubscriptionResponse represents a subscription response.
|
||||
|
||||
@@ -653,16 +653,6 @@ func (e *Exchange) wsProcessOrderbook(assetType asset.Item, resp *WebsocketRespo
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
asks := make([]orderbook.Level, len(result.Asks))
|
||||
for i := range result.Asks {
|
||||
asks[i].Price = result.Asks[i][0].Float64()
|
||||
asks[i].Amount = result.Asks[i][1].Float64()
|
||||
}
|
||||
bids := make([]orderbook.Level, len(result.Bids))
|
||||
for i := range result.Bids {
|
||||
bids[i].Price = result.Bids[i][0].Float64()
|
||||
bids[i].Amount = result.Bids[i][1].Float64()
|
||||
}
|
||||
|
||||
if resp.Type == "snapshot" {
|
||||
return e.Websocket.Orderbook.LoadSnapshot(&orderbook.Book{
|
||||
@@ -672,14 +662,14 @@ func (e *Exchange) wsProcessOrderbook(assetType asset.Item, resp *WebsocketRespo
|
||||
LastUpdated: resp.OrderbookLastUpdated.Time(),
|
||||
LastUpdateID: result.UpdateID,
|
||||
LastPushed: resp.PushTimestamp.Time(),
|
||||
Asks: asks,
|
||||
Bids: bids,
|
||||
Asks: result.Asks.Levels(),
|
||||
Bids: result.Bids.Levels(),
|
||||
})
|
||||
}
|
||||
return e.Websocket.Orderbook.Update(&orderbook.Update{
|
||||
Pair: cp,
|
||||
Asks: asks,
|
||||
Bids: bids,
|
||||
Asks: result.Asks.Levels(),
|
||||
Bids: result.Bids.Levels(),
|
||||
Asset: assetType,
|
||||
UpdateID: result.UpdateID,
|
||||
UpdateTime: resp.OrderbookLastUpdated.Time(),
|
||||
|
||||
@@ -3676,3 +3676,13 @@ func TestMarshalJSONNumber(t *testing.T) {
|
||||
assert.Equal(t, tc.expected, string(payload), "MarshalJSON should return expected value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalJSONOrderbookLevels(t *testing.T) {
|
||||
t.Parallel()
|
||||
var ob OrderbookLevels
|
||||
require.NoError(t, ob.UnmarshalJSON([]byte(`[{"p":"123.45","s":"0.001"}]`)))
|
||||
assert.Equal(t, 123.45, ob[0].Price, "Price should be correct")
|
||||
assert.Equal(t, 0.001, ob[0].Amount, "Amount should be correct")
|
||||
|
||||
require.Error(t, ob.UnmarshalJSON([]byte(`["p":"123.45","s":"0.001"]`)))
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/types"
|
||||
)
|
||||
|
||||
@@ -519,26 +520,41 @@ type Ticker struct {
|
||||
|
||||
// OrderbookData holds orderbook ask and bid datas.
|
||||
type OrderbookData struct {
|
||||
ID int64 `json:"id"`
|
||||
Current types.Time `json:"current"` // The timestamp of the response data being generated (in milliseconds)
|
||||
Update types.Time `json:"update"` // The timestamp of when the orderbook last changed (in milliseconds)
|
||||
Asks [][2]types.Number `json:"asks"`
|
||||
Bids [][2]types.Number `json:"bids"`
|
||||
ID int64 `json:"id"`
|
||||
Current types.Time `json:"current"` // The timestamp of the response data being generated (in milliseconds)
|
||||
Update types.Time `json:"update"` // The timestamp of when the orderbook last changed (in milliseconds)
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"asks"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"bids"`
|
||||
}
|
||||
|
||||
// MakeOrderbook converts OrderbookData into an Orderbook
|
||||
func (a *OrderbookData) MakeOrderbook() *Orderbook {
|
||||
asks := make([]OrderbookItem, len(a.Asks))
|
||||
for x := range a.Asks {
|
||||
asks[x].Price = a.Asks[x][0]
|
||||
asks[x].Amount = a.Asks[x][1]
|
||||
return &Orderbook{ID: a.ID, Current: a.Current, Update: a.Update, Asks: OrderbookLevels(a.Asks.Levels()), Bids: OrderbookLevels(a.Bids.Levels())}
|
||||
}
|
||||
|
||||
// OrderbookLevels represents a slice of orderbook levels.
|
||||
type OrderbookLevels orderbook.Levels
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface for OrderbookLevels.
|
||||
func (o *OrderbookLevels) UnmarshalJSON(data []byte) error {
|
||||
var levels []OrderbookItem
|
||||
if err := json.Unmarshal(data, &levels); err != nil {
|
||||
return err
|
||||
}
|
||||
bids := make([]OrderbookItem, len(a.Bids))
|
||||
for x := range a.Bids {
|
||||
bids[x].Price = a.Bids[x][0]
|
||||
bids[x].Amount = a.Bids[x][1]
|
||||
|
||||
*o = make(OrderbookLevels, len(levels))
|
||||
for x := range levels {
|
||||
(*o)[x] = orderbook.Level{
|
||||
Price: levels[x].Price.Float64(),
|
||||
Amount: levels[x].Amount.Float64(),
|
||||
}
|
||||
}
|
||||
return &Orderbook{ID: a.ID, Current: a.Current, Update: a.Update, Asks: asks, Bids: bids}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Levels converts OrderbookLevels to orderbook.Levels.
|
||||
func (o *OrderbookLevels) Levels() orderbook.Levels {
|
||||
return orderbook.Levels(*o)
|
||||
}
|
||||
|
||||
// OrderbookItem stores an orderbook item
|
||||
@@ -552,8 +568,8 @@ type Orderbook struct {
|
||||
ID int64 `json:"id"`
|
||||
Current types.Time `json:"current"` // The timestamp of the response data being generated (in milliseconds)
|
||||
Update types.Time `json:"update"` // The timestamp of when the orderbook last changed (in milliseconds)
|
||||
Bids []OrderbookItem `json:"bids"`
|
||||
Asks []OrderbookItem `json:"asks"`
|
||||
Bids OrderbookLevels `json:"bids"`
|
||||
Asks OrderbookLevels `json:"asks"`
|
||||
}
|
||||
|
||||
// Trade represents market trade.
|
||||
@@ -2053,21 +2069,21 @@ type WsOrderbookTickerData struct {
|
||||
|
||||
// WsOrderbookUpdate represents websocket orderbook update push data
|
||||
type WsOrderbookUpdate struct {
|
||||
UpdateTime types.Time `json:"t"`
|
||||
Pair currency.Pair `json:"s"`
|
||||
FirstUpdateID int64 `json:"U"` // First update order book id in this event since last update
|
||||
LastUpdateID int64 `json:"u"`
|
||||
Bids [][2]types.Number `json:"b"`
|
||||
Asks [][2]types.Number `json:"a"`
|
||||
UpdateTime types.Time `json:"t"`
|
||||
Pair currency.Pair `json:"s"`
|
||||
FirstUpdateID int64 `json:"U"` // First update order book id in this event since last update
|
||||
LastUpdateID int64 `json:"u"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"b"`
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"a"`
|
||||
}
|
||||
|
||||
// WsOrderbookSnapshot represents a websocket orderbook snapshot push data
|
||||
type WsOrderbookSnapshot struct {
|
||||
UpdateTime types.Time `json:"t"`
|
||||
LastUpdateID int64 `json:"lastUpdateId"`
|
||||
CurrencyPair currency.Pair `json:"s"`
|
||||
Bids [][2]types.Number `json:"bids"`
|
||||
Asks [][2]types.Number `json:"asks"`
|
||||
UpdateTime types.Time `json:"t"`
|
||||
LastUpdateID int64 `json:"lastUpdateId"`
|
||||
CurrencyPair currency.Pair `json:"s"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"bids"`
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"asks"`
|
||||
}
|
||||
|
||||
// WsSpotOrder represents an order push data through the websocket channel.
|
||||
|
||||
@@ -373,24 +373,14 @@ func (e *Exchange) processOrderbookUpdate(ctx context.Context, incoming []byte,
|
||||
if err := json.Unmarshal(incoming, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
asks := make([]orderbook.Level, len(data.Asks))
|
||||
for x := range data.Asks {
|
||||
asks[x].Price = data.Asks[x][0].Float64()
|
||||
asks[x].Amount = data.Asks[x][1].Float64()
|
||||
}
|
||||
bids := make([]orderbook.Level, len(data.Bids))
|
||||
for x := range data.Bids {
|
||||
bids[x].Price = data.Bids[x][0].Float64()
|
||||
bids[x].Amount = data.Bids[x][1].Float64()
|
||||
}
|
||||
return e.wsOBUpdateMgr.ProcessOrderbookUpdate(ctx, e, data.FirstUpdateID, &orderbook.Update{
|
||||
UpdateID: data.LastUpdateID,
|
||||
UpdateTime: data.UpdateTime.Time(),
|
||||
LastPushed: lastPushed,
|
||||
Pair: data.Pair,
|
||||
Asset: asset.Spot,
|
||||
Asks: asks,
|
||||
Bids: bids,
|
||||
Asks: data.Asks.Levels(),
|
||||
Bids: data.Bids.Levels(),
|
||||
AllowEmpty: true,
|
||||
})
|
||||
}
|
||||
@@ -401,17 +391,6 @@ func (e *Exchange) processOrderbookSnapshot(incoming []byte, lastPushed time.Tim
|
||||
return err
|
||||
}
|
||||
|
||||
asks := make([]orderbook.Level, len(data.Asks))
|
||||
for x := range data.Asks {
|
||||
asks[x].Price = data.Asks[x][0].Float64()
|
||||
asks[x].Amount = data.Asks[x][1].Float64()
|
||||
}
|
||||
bids := make([]orderbook.Level, len(data.Bids))
|
||||
for x := range data.Bids {
|
||||
bids[x].Price = data.Bids[x][0].Float64()
|
||||
bids[x].Amount = data.Bids[x][1].Float64()
|
||||
}
|
||||
|
||||
for _, a := range standardMarginAssetTypes {
|
||||
if enabled, _ := e.CurrencyPairs.IsPairEnabled(data.CurrencyPair, a); enabled {
|
||||
if err := e.Websocket.Orderbook.LoadSnapshot(&orderbook.Book{
|
||||
@@ -420,8 +399,8 @@ func (e *Exchange) processOrderbookSnapshot(incoming []byte, lastPushed time.Tim
|
||||
Asset: a,
|
||||
LastUpdated: data.UpdateTime.Time(),
|
||||
LastPushed: lastPushed,
|
||||
Bids: bids,
|
||||
Asks: asks,
|
||||
Bids: data.Bids.Levels(),
|
||||
Asks: data.Asks.Levels(),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -670,34 +670,24 @@ func (e *Exchange) UpdateOrderbookWithLimit(ctx context.Context, p currency.Pair
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
book := &orderbook.Book{
|
||||
|
||||
ob := &orderbook.Book{
|
||||
Exchange: e.Name,
|
||||
Asset: a,
|
||||
ValidateOrderbook: e.ValidateOrderbook,
|
||||
Pair: p.Upper(),
|
||||
Pair: p,
|
||||
LastUpdateID: o.ID,
|
||||
LastUpdated: o.Update.Time(),
|
||||
LastPushed: o.Current.Time(),
|
||||
Bids: o.Bids.Levels(),
|
||||
Asks: o.Asks.Levels(),
|
||||
}
|
||||
book.Bids = make(orderbook.Levels, len(o.Bids))
|
||||
for x := range o.Bids {
|
||||
book.Bids[x] = orderbook.Level{
|
||||
Amount: o.Bids[x].Amount.Float64(),
|
||||
Price: o.Bids[x].Price.Float64(),
|
||||
}
|
||||
|
||||
if err := ob.Process(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
book.Asks = make(orderbook.Levels, len(o.Asks))
|
||||
for x := range o.Asks {
|
||||
book.Asks[x] = orderbook.Level{
|
||||
Amount: o.Asks[x].Amount.Float64(),
|
||||
Price: o.Asks[x].Price.Float64(),
|
||||
}
|
||||
}
|
||||
err = book.Process()
|
||||
if err != nil {
|
||||
return book, err
|
||||
}
|
||||
return orderbook.Get(e.Name, book.Pair, a)
|
||||
|
||||
return orderbook.Get(e.Name, p, a)
|
||||
}
|
||||
|
||||
// UpdateAccountInfo retrieves balances for all enabled currencies for the
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/types"
|
||||
)
|
||||
@@ -99,33 +98,6 @@ func (e *Exchange) GetMarketList(ctx context.Context) ([]string, error) {
|
||||
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, marketListEPL, "/v1/markets", &resp)
|
||||
}
|
||||
|
||||
// processOB constructs an orderbook.Level instances from slice of numbers.
|
||||
func processOB(ob [][2]types.Number) []orderbook.Level {
|
||||
o := make([]orderbook.Level, len(ob))
|
||||
for x := range ob {
|
||||
o[x].Amount = ob[x][1].Float64()
|
||||
o[x].Price = ob[x][0].Float64()
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// constructOrderbook parse checks and constructs an *Orderbook instance from *orderbookResponse.
|
||||
func constructOrderbook(o *orderbookResponse) (*Orderbook, error) {
|
||||
s := Orderbook{
|
||||
Bids: processOB(o.Bids),
|
||||
Asks: processOB(o.Asks),
|
||||
Time: o.Time.Time(),
|
||||
}
|
||||
if o.Sequence != "" {
|
||||
var err error
|
||||
s.Sequence, err = strconv.ParseInt(o.Sequence, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
// GetPartOrderbook20 gets orderbook for a specified pair with depth 20
|
||||
func (e *Exchange) GetPartOrderbook20(ctx context.Context, symbol string) (*Orderbook, error) {
|
||||
if symbol == "" {
|
||||
@@ -134,11 +106,10 @@ func (e *Exchange) GetPartOrderbook20(ctx context.Context, symbol string) (*Orde
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
var o *orderbookResponse
|
||||
err := e.SendHTTPRequest(ctx, exchange.RestSpot, partOrderbook20EPL, common.EncodeURLValues("/v1/market/orderbook/level2_20", params), &o)
|
||||
if err != nil {
|
||||
if err := e.SendHTTPRequest(ctx, exchange.RestSpot, partOrderbook20EPL, common.EncodeURLValues("/v1/market/orderbook/level2_20", params), &o); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return constructOrderbook(o)
|
||||
return &Orderbook{Asks: o.Asks, Bids: o.Bids, Time: o.Time.Time(), Sequence: o.Sequence.Int64()}, nil
|
||||
}
|
||||
|
||||
// GetPartOrderbook100 gets orderbook for a specified pair with depth 100
|
||||
@@ -149,11 +120,10 @@ func (e *Exchange) GetPartOrderbook100(ctx context.Context, symbol string) (*Ord
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
var o *orderbookResponse
|
||||
err := e.SendHTTPRequest(ctx, exchange.RestSpot, partOrderbook100EPL, common.EncodeURLValues("/v1/market/orderbook/level2_100", params), &o)
|
||||
if err != nil {
|
||||
if err := e.SendHTTPRequest(ctx, exchange.RestSpot, partOrderbook100EPL, common.EncodeURLValues("/v1/market/orderbook/level2_100", params), &o); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return constructOrderbook(o)
|
||||
return &Orderbook{Asks: o.Asks, Bids: o.Bids, Time: o.Time.Time(), Sequence: o.Sequence.Int64()}, nil
|
||||
}
|
||||
|
||||
// GetOrderbook gets full orderbook for a specified pair
|
||||
@@ -164,11 +134,10 @@ func (e *Exchange) GetOrderbook(ctx context.Context, symbol string) (*Orderbook,
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
var o *orderbookResponse
|
||||
err := e.SendAuthHTTPRequest(ctx, exchange.RestSpot, fullOrderbookEPL, http.MethodGet, common.EncodeURLValues("/v3/market/orderbook/level2", params), nil, &o)
|
||||
if err != nil {
|
||||
if err := e.SendAuthHTTPRequest(ctx, exchange.RestSpot, fullOrderbookEPL, http.MethodGet, common.EncodeURLValues("/v3/market/orderbook/level2", params), nil, &o); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return constructOrderbook(o)
|
||||
return &Orderbook{Asks: o.Asks, Bids: o.Bids, Time: o.Time.Time(), Sequence: o.Sequence.Int64()}, nil
|
||||
}
|
||||
|
||||
// GetTradeHistory gets trade history of the specified pair
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-corp/gocryptotrader/types"
|
||||
)
|
||||
@@ -117,12 +116,11 @@ func (e *Exchange) GetFuturesOrderbook(ctx context.Context, symbol string) (*Ord
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
var o futuresOrderbookResponse
|
||||
err := e.SendHTTPRequest(ctx, exchange.RestFutures, futuresOrderbookEPL, common.EncodeURLValues("/v1/level2/snapshot", params), &o)
|
||||
if err != nil {
|
||||
var o *futuresOrderbookResponse
|
||||
if err := e.SendHTTPRequest(ctx, exchange.RestFutures, futuresOrderbookEPL, common.EncodeURLValues("/v1/level2/snapshot", params), &o); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return constructFuturesOrderbook(&o), nil
|
||||
return unifyFuturesOrderbook(o), nil
|
||||
}
|
||||
|
||||
// GetFuturesPartOrderbook20 gets orderbook for a specified symbol with depth 20
|
||||
@@ -132,12 +130,11 @@ func (e *Exchange) GetFuturesPartOrderbook20(ctx context.Context, symbol string)
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
var o futuresOrderbookResponse
|
||||
err := e.SendHTTPRequest(ctx, exchange.RestFutures, futuresPartOrderbookDepth20EPL, common.EncodeURLValues("/v1/level2/depth20", params), &o)
|
||||
if err != nil {
|
||||
var o *futuresOrderbookResponse
|
||||
if err := e.SendHTTPRequest(ctx, exchange.RestFutures, futuresPartOrderbookDepth20EPL, common.EncodeURLValues("/v1/level2/depth20", params), &o); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return constructFuturesOrderbook(&o), nil
|
||||
return unifyFuturesOrderbook(o), nil
|
||||
}
|
||||
|
||||
// GetFuturesPartOrderbook100 gets orderbook for a specified symbol with depth 100
|
||||
@@ -147,12 +144,15 @@ func (e *Exchange) GetFuturesPartOrderbook100(ctx context.Context, symbol string
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
var o futuresOrderbookResponse
|
||||
err := e.SendHTTPRequest(ctx, exchange.RestFutures, futuresPartOrderbookDepth100EPL, common.EncodeURLValues("/v1/level2/depth100", params), &o)
|
||||
if err != nil {
|
||||
var o *futuresOrderbookResponse
|
||||
if err := e.SendHTTPRequest(ctx, exchange.RestFutures, futuresPartOrderbookDepth100EPL, common.EncodeURLValues("/v1/level2/depth100", params), &o); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return constructFuturesOrderbook(&o), nil
|
||||
return unifyFuturesOrderbook(o), nil
|
||||
}
|
||||
|
||||
func unifyFuturesOrderbook(o *futuresOrderbookResponse) *Orderbook {
|
||||
return &Orderbook{Bids: o.Bids.Levels(), Asks: o.Asks.Levels(), Sequence: o.Sequence, Time: o.Time.Time()}
|
||||
}
|
||||
|
||||
// GetFuturesTradeHistory get last 100 trades for symbol
|
||||
@@ -822,26 +822,6 @@ func (e *Exchange) GetFuturesTransferOutList(ctx context.Context, ccy currency.C
|
||||
return resp, e.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresTransferOutListEPL, http.MethodGet, common.EncodeURLValues("/v1/transfer-list", params), nil, &resp)
|
||||
}
|
||||
|
||||
func processFuturesOB(ob [][2]float64) []orderbook.Level {
|
||||
o := make([]orderbook.Level, len(ob))
|
||||
for x := range ob {
|
||||
o[x] = orderbook.Level{
|
||||
Price: ob[x][0],
|
||||
Amount: ob[x][1],
|
||||
}
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
func constructFuturesOrderbook(o *futuresOrderbookResponse) *Orderbook {
|
||||
return &Orderbook{
|
||||
Bids: processFuturesOB(o.Bids),
|
||||
Asks: processFuturesOB(o.Asks),
|
||||
Sequence: o.Sequence,
|
||||
Time: o.Time.Time(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetFuturesTradingPairsActualFees retrieves the actual fee rate of the trading pair. The fee rate of your sub-account is the same as that of the master account
|
||||
func (e *Exchange) GetFuturesTradingPairsActualFees(ctx context.Context, symbol string) (*TradingPairFee, error) {
|
||||
if symbol == "" {
|
||||
|
||||
@@ -3,6 +3,7 @@ package kucoin
|
||||
import (
|
||||
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/types"
|
||||
)
|
||||
|
||||
@@ -85,11 +86,11 @@ type FuturesTicker struct {
|
||||
}
|
||||
|
||||
type futuresOrderbookResponse struct {
|
||||
Asks [][2]float64 `json:"asks"`
|
||||
Bids [][2]float64 `json:"bids"`
|
||||
Time types.Time `json:"ts"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Symbol string `json:"symbol"`
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"asks"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"bids"`
|
||||
Time types.Time `json:"ts"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Symbol string `json:"symbol"`
|
||||
}
|
||||
|
||||
// FuturesTrade stores trade data
|
||||
|
||||
@@ -162,10 +162,10 @@ type Orderbook struct {
|
||||
}
|
||||
|
||||
type orderbookResponse struct {
|
||||
Asks [][2]types.Number `json:"asks"`
|
||||
Bids [][2]types.Number `json:"bids"`
|
||||
Time types.Time `json:"time"`
|
||||
Sequence string `json:"sequence"`
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"asks"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"bids"`
|
||||
Time types.Time `json:"time"`
|
||||
Sequence types.Number `json:"sequence"`
|
||||
}
|
||||
|
||||
// Trade stores trade data
|
||||
@@ -1537,35 +1537,11 @@ type WsOrderbookLevel5 struct {
|
||||
|
||||
// WsOrderbookLevel5Response represents a response data for an orderbook push data with depth level 5
|
||||
type WsOrderbookLevel5Response struct {
|
||||
Sequence int64 `json:"sequence"`
|
||||
Bids [][2]types.Number `json:"bids"`
|
||||
Asks [][2]types.Number `json:"asks"`
|
||||
PushTimestamp types.Time `json:"ts"`
|
||||
Timestamp types.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
// ExtractOrderbookItems returns WsOrderbookLevel5 instance from WsOrderbookLevel5Response
|
||||
func (a *WsOrderbookLevel5Response) ExtractOrderbookItems() *WsOrderbookLevel5 {
|
||||
resp := WsOrderbookLevel5{
|
||||
Timestamp: a.Timestamp,
|
||||
Sequence: a.Sequence,
|
||||
PushTimestamp: a.PushTimestamp,
|
||||
}
|
||||
resp.Asks = make([]orderbook.Level, len(a.Asks))
|
||||
for x := range a.Asks {
|
||||
resp.Asks[x] = orderbook.Level{
|
||||
Price: a.Asks[x][0].Float64(),
|
||||
Amount: a.Asks[x][1].Float64(),
|
||||
}
|
||||
}
|
||||
resp.Bids = make([]orderbook.Level, len(a.Bids))
|
||||
for x := range a.Bids {
|
||||
resp.Bids[x] = orderbook.Level{
|
||||
Price: a.Bids[x][0].Float64(),
|
||||
Amount: a.Bids[x][1].Float64(),
|
||||
}
|
||||
}
|
||||
return &resp
|
||||
Sequence int64 `json:"sequence"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"bids"`
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"asks"`
|
||||
PushTimestamp types.Time `json:"ts"`
|
||||
Timestamp types.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
// WsFundingRate represents the funding rate push data information through the websocket channel
|
||||
@@ -2095,9 +2071,9 @@ type HFMarginOrderTransaction struct {
|
||||
// Level2Depth5Or20 stores the orderbook data for the level 5 or level 20
|
||||
// orderbook
|
||||
type Level2Depth5Or20 struct {
|
||||
Asks [][2]types.Number `json:"asks"`
|
||||
Bids [][2]types.Number `json:"bids"`
|
||||
Timestamp types.Time `json:"timestamp"`
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"asks"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"bids"`
|
||||
Timestamp types.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
// TradingPairFee represents actual fee information of a trading fee
|
||||
|
||||
@@ -533,16 +533,11 @@ func (e *Exchange) ensureFuturesOrderbookSnapshotLoaded(ctx context.Context, sym
|
||||
|
||||
// processFuturesOrderbookSnapshot processes a futures account orderbook websocket update.
|
||||
func (e *Exchange) processFuturesOrderbookSnapshot(respData []byte, instrument string) error {
|
||||
response := WsOrderbookLevel5Response{}
|
||||
if err := json.Unmarshal(respData, &response); err != nil {
|
||||
var resp WsOrderbookLevel5Response
|
||||
if err := json.Unmarshal(respData, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
resp := response.ExtractOrderbookItems()
|
||||
enabledPairs, err := e.GetEnabledPairs(asset.Futures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cp, err := enabledPairs.DeriveFrom(instrument)
|
||||
pair, err := e.MatchSymbolWithAvailablePairs(instrument, asset.Futures, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -550,9 +545,9 @@ func (e *Exchange) processFuturesOrderbookSnapshot(respData []byte, instrument s
|
||||
UpdateID: resp.Sequence,
|
||||
UpdateTime: resp.Timestamp.Time(),
|
||||
Asset: asset.Futures,
|
||||
Bids: resp.Bids,
|
||||
Asks: resp.Asks,
|
||||
Pair: cp,
|
||||
Bids: resp.Bids.Levels(),
|
||||
Asks: resp.Asks.Levels(),
|
||||
Pair: pair,
|
||||
SkipOutOfOrderLastUpdateID: true,
|
||||
})
|
||||
}
|
||||
@@ -942,9 +937,8 @@ func (e *Exchange) updateLocalBuffer(wsdp *WsOrderbook, assetType asset.Item) (b
|
||||
|
||||
// processOrderbook processes orderbook data for a specific symbol.
|
||||
func (e *Exchange) processOrderbook(respData []byte, symbol, topic string) error {
|
||||
var response Level2Depth5Or20
|
||||
err := json.Unmarshal(respData, &response)
|
||||
if err != nil {
|
||||
var resp Level2Depth5Or20
|
||||
if err := json.Unmarshal(respData, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -953,32 +947,20 @@ func (e *Exchange) processOrderbook(respData []byte, symbol, topic string) error
|
||||
return err
|
||||
}
|
||||
|
||||
asks := make([]orderbook.Level, len(response.Asks))
|
||||
for x := range response.Asks {
|
||||
asks[x].Price = response.Asks[x][0].Float64()
|
||||
asks[x].Amount = response.Asks[x][1].Float64()
|
||||
}
|
||||
|
||||
bids := make([]orderbook.Level, len(response.Bids))
|
||||
for x := range response.Bids {
|
||||
bids[x].Price = response.Bids[x][0].Float64()
|
||||
bids[x].Amount = response.Bids[x][1].Float64()
|
||||
}
|
||||
|
||||
assets, err := e.CalculateAssets(topic, pair)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lastUpdatedTime := response.Timestamp.Time()
|
||||
if response.Timestamp.Time().IsZero() {
|
||||
lastUpdatedTime := resp.Timestamp.Time()
|
||||
if lastUpdatedTime.IsZero() {
|
||||
lastUpdatedTime = time.Now()
|
||||
}
|
||||
for x := range assets {
|
||||
err = e.Websocket.Orderbook.LoadSnapshot(&orderbook.Book{
|
||||
Exchange: e.Name,
|
||||
Asks: asks,
|
||||
Bids: bids,
|
||||
Asks: resp.Asks.Levels(),
|
||||
Bids: resp.Bids.Levels(),
|
||||
Pair: pair,
|
||||
Asset: assets[x],
|
||||
LastUpdated: lastUpdatedTime,
|
||||
@@ -1270,9 +1252,7 @@ func (e *Exchange) SynchroniseWebsocketOrderbook(ctx context.Context) {
|
||||
|
||||
// SeedLocalCache seeds depth data
|
||||
func (e *Exchange) SeedLocalCache(ctx context.Context, p currency.Pair, assetType asset.Item) error {
|
||||
var ob *Orderbook
|
||||
var err error
|
||||
ob, err = e.GetPartOrderbook100(ctx, p.String())
|
||||
ob, err := e.GetPartOrderbook100(ctx, p.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1284,29 +1264,16 @@ func (e *Exchange) SeedLocalCache(ctx context.Context, p currency.Pair, assetTyp
|
||||
|
||||
// SeedLocalCacheWithBook seeds the local orderbook cache
|
||||
func (e *Exchange) SeedLocalCacheWithBook(p currency.Pair, orderbookNew *Orderbook, assetType asset.Item) error {
|
||||
newOrderBook := orderbook.Book{
|
||||
return e.Websocket.Orderbook.LoadSnapshot(&orderbook.Book{
|
||||
Pair: p,
|
||||
Asset: assetType,
|
||||
Exchange: e.Name,
|
||||
LastUpdated: time.Now(),
|
||||
LastUpdated: orderbookNew.Time,
|
||||
LastUpdateID: orderbookNew.Sequence,
|
||||
ValidateOrderbook: e.ValidateOrderbook,
|
||||
Bids: make(orderbook.Levels, len(orderbookNew.Bids)),
|
||||
Asks: make(orderbook.Levels, len(orderbookNew.Asks)),
|
||||
}
|
||||
for i := range orderbookNew.Bids {
|
||||
newOrderBook.Bids[i] = orderbook.Level{
|
||||
Amount: orderbookNew.Bids[i].Amount,
|
||||
Price: orderbookNew.Bids[i].Price,
|
||||
}
|
||||
}
|
||||
for i := range orderbookNew.Asks {
|
||||
newOrderBook.Asks[i] = orderbook.Level{
|
||||
Amount: orderbookNew.Asks[i].Amount,
|
||||
Price: orderbookNew.Asks[i].Price,
|
||||
}
|
||||
}
|
||||
return e.Websocket.Orderbook.LoadSnapshot(&newOrderBook)
|
||||
Bids: orderbookNew.Bids,
|
||||
Asks: orderbookNew.Asks,
|
||||
})
|
||||
}
|
||||
|
||||
// processJob fetches and processes orderbook updates
|
||||
|
||||
@@ -368,41 +368,42 @@ func (e *Exchange) UpdateTickers(ctx context.Context, assetType asset.Item) erro
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (e *Exchange) UpdateOrderbook(ctx context.Context, pair currency.Pair, assetType asset.Item) (*orderbook.Book, error) {
|
||||
err := e.CurrencyPairs.IsAssetEnabled(assetType)
|
||||
func (e *Exchange) UpdateOrderbook(ctx context.Context, p currency.Pair, a asset.Item) (*orderbook.Book, error) {
|
||||
err := e.CurrencyPairs.IsAssetEnabled(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pair, err = e.FormatExchangeCurrency(pair, assetType)
|
||||
p, err = e.FormatExchangeCurrency(p, a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ordBook *Orderbook
|
||||
switch assetType {
|
||||
switch a {
|
||||
case asset.Futures:
|
||||
ordBook, err = e.GetFuturesOrderbook(ctx, pair.String())
|
||||
ordBook, err = e.GetFuturesOrderbook(ctx, p.String())
|
||||
case asset.Spot, asset.Margin:
|
||||
ordBook, err = e.GetPartOrderbook100(ctx, pair.String())
|
||||
ordBook, err = e.GetPartOrderbook100(ctx, p.String())
|
||||
default:
|
||||
return nil, fmt.Errorf("%w %v", asset.ErrNotSupported, assetType)
|
||||
return nil, fmt.Errorf("%w %v", asset.ErrNotSupported, a)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
book := &orderbook.Book{
|
||||
ob := &orderbook.Book{
|
||||
Exchange: e.Name,
|
||||
Pair: pair,
|
||||
Asset: assetType,
|
||||
Pair: p,
|
||||
Asset: a,
|
||||
ValidateOrderbook: e.ValidateOrderbook,
|
||||
Asks: ordBook.Asks,
|
||||
Bids: ordBook.Bids,
|
||||
}
|
||||
err = book.Process()
|
||||
if err != nil {
|
||||
return book, err
|
||||
|
||||
if err := ob.Process(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return orderbook.Get(e.Name, pair, assetType)
|
||||
|
||||
return orderbook.Get(e.Name, p, a)
|
||||
}
|
||||
|
||||
// UpdateAccountInfo retrieves balances for all enabled currencies
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/types"
|
||||
)
|
||||
|
||||
@@ -29,9 +30,9 @@ type TickerResponse struct {
|
||||
type MarketDepthResponse struct {
|
||||
ErrCapture
|
||||
Data struct {
|
||||
Asks [][2]types.Number `json:"asks"`
|
||||
Bids [][2]types.Number `json:"bids"`
|
||||
Timestamp types.Time `json:"timestamp"`
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"asks"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"bids"`
|
||||
Timestamp types.Time `json:"timestamp"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
|
||||
@@ -210,28 +210,17 @@ func (e *Exchange) UpdateOrderbook(ctx context.Context, p currency.Pair, assetTy
|
||||
return nil, err
|
||||
}
|
||||
|
||||
book := &orderbook.Book{
|
||||
ob := &orderbook.Book{
|
||||
Exchange: e.Name,
|
||||
Pair: p,
|
||||
Asset: assetType,
|
||||
ValidateOrderbook: e.ValidateOrderbook,
|
||||
Asks: make(orderbook.Levels, len(d.Data.Asks)),
|
||||
Bids: make(orderbook.Levels, len(d.Data.Bids)),
|
||||
Asks: d.Data.Asks.Levels(),
|
||||
Bids: d.Data.Bids.Levels(),
|
||||
}
|
||||
|
||||
for i := range d.Data.Asks {
|
||||
book.Asks[i].Price = d.Data.Asks[i][0].Float64()
|
||||
book.Asks[i].Amount = d.Data.Asks[i][1].Float64()
|
||||
}
|
||||
for i := range d.Data.Bids {
|
||||
book.Bids[i].Price = d.Data.Bids[i][0].Float64()
|
||||
book.Bids[i].Amount = d.Data.Bids[i][1].Float64()
|
||||
}
|
||||
|
||||
if err := book.Process(); err != nil {
|
||||
if err := ob.Process(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return orderbook.Get(e.Name, p, assetType)
|
||||
}
|
||||
|
||||
|
||||
@@ -562,3 +562,20 @@ func BenchmarkProcess(b *testing.B) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLevelsArrayPriceAmountUnmarshalJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var asks LevelsArrayPriceAmount
|
||||
err := asks.UnmarshalJSON([]byte(`[[1,2],["3","4"]]`))
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, asks, 2)
|
||||
assert.Equal(t, 1.0, asks[0].Price)
|
||||
assert.Equal(t, 2.0, asks[0].Amount)
|
||||
assert.Equal(t, 3.0, asks[1].Price)
|
||||
assert.Equal(t, 4.0, asks[1].Amount)
|
||||
assert.Equal(t, 2, len(asks.Levels()))
|
||||
|
||||
err = asks.UnmarshalJSON([]byte(`invalid`))
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/common/key"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/dispatch"
|
||||
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -184,3 +186,26 @@ type SideAmounts struct {
|
||||
QuoteValue float64
|
||||
BaseAmount float64
|
||||
}
|
||||
|
||||
// LevelsArrayPriceAmount used to unmarshal orderbook levels from JSON slice of arrays
|
||||
// e.g. [[price, amount], [price, amount]] or [][2]types.Number type declaration
|
||||
type LevelsArrayPriceAmount Levels
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler
|
||||
func (l *LevelsArrayPriceAmount) UnmarshalJSON(data []byte) error {
|
||||
var v [][2]types.Number
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
*l = make(LevelsArrayPriceAmount, len(v))
|
||||
for x := range v {
|
||||
(*l)[x].Price = v[x][0].Float64()
|
||||
(*l)[x].Amount = v[x][1].Float64()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Levels converts the LevelsArrayPriceAmount to a orderbook.Levels type
|
||||
func (l *LevelsArrayPriceAmount) Levels() Levels {
|
||||
return Levels(*l)
|
||||
}
|
||||
|
||||
@@ -101,56 +101,28 @@ func (e *Exchange) GetOrderbook(ctx context.Context, currencyPair string, depth
|
||||
vals.Set("currencyPair", currencyPair)
|
||||
resp := OrderbookResponse{}
|
||||
path := "/public?command=returnOrderBook&" + vals.Encode()
|
||||
err := e.SendHTTPRequest(ctx, exchange.RestSpot, path, &resp)
|
||||
if err != nil {
|
||||
if err := e.SendHTTPRequest(ctx, exchange.RestSpot, path, &resp); err != nil {
|
||||
return oba, err
|
||||
}
|
||||
if resp.Error != "" {
|
||||
return oba, fmt.Errorf("%s GetOrderbook() error: %s", e.Name, resp.Error)
|
||||
}
|
||||
ob := Orderbook{
|
||||
Bids: make([]OrderbookItem, len(resp.Bids)),
|
||||
Asks: make([]OrderbookItem, len(resp.Asks)),
|
||||
oba.Data[currencyPair] = Orderbook{
|
||||
Bids: resp.Bids.Levels(),
|
||||
Asks: resp.Asks.Levels(),
|
||||
}
|
||||
for x := range resp.Asks {
|
||||
ob.Asks[x] = OrderbookItem{
|
||||
Price: resp.Asks[x][0].Float64(),
|
||||
Amount: resp.Asks[x][1].Float64(),
|
||||
}
|
||||
}
|
||||
for x := range resp.Bids {
|
||||
ob.Bids[x] = OrderbookItem{
|
||||
Price: resp.Bids[x][0].Float64(),
|
||||
Amount: resp.Bids[x][1].Float64(),
|
||||
}
|
||||
}
|
||||
oba.Data[currencyPair] = ob
|
||||
} else {
|
||||
vals.Set("currencyPair", "all")
|
||||
resp := OrderbookResponseAll{}
|
||||
path := "/public?command=returnOrderBook&" + vals.Encode()
|
||||
err := e.SendHTTPRequest(ctx, exchange.RestSpot, path, &resp.Data)
|
||||
if err != nil {
|
||||
if err := e.SendHTTPRequest(ctx, exchange.RestSpot, path, &resp.Data); err != nil {
|
||||
return oba, err
|
||||
}
|
||||
for currency, orderbook := range resp.Data {
|
||||
ob := Orderbook{
|
||||
Bids: make([]OrderbookItem, len(orderbook.Bids)),
|
||||
Asks: make([]OrderbookItem, len(orderbook.Asks)),
|
||||
oba.Data[currency] = Orderbook{
|
||||
Bids: orderbook.Bids.Levels(),
|
||||
Asks: orderbook.Asks.Levels(),
|
||||
}
|
||||
for x := range orderbook.Asks {
|
||||
ob.Asks[x] = OrderbookItem{
|
||||
Price: orderbook.Asks[x][0].Float64(),
|
||||
Amount: orderbook.Asks[x][1].Float64(),
|
||||
}
|
||||
}
|
||||
for x := range orderbook.Bids {
|
||||
ob.Bids[x] = OrderbookItem{
|
||||
Price: orderbook.Bids[x][0].Float64(),
|
||||
Amount: orderbook.Bids[x][1].Float64(),
|
||||
}
|
||||
}
|
||||
oba.Data[currency] = ob
|
||||
}
|
||||
}
|
||||
return oba, nil
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/types"
|
||||
)
|
||||
|
||||
@@ -33,17 +34,11 @@ type CompleteBalances map[string]CompleteBalance
|
||||
|
||||
// OrderbookResponse is a sub-type for orderbooks
|
||||
type OrderbookResponse struct {
|
||||
Asks [][2]types.Number `json:"asks"`
|
||||
Bids [][2]types.Number `json:"bids"`
|
||||
IsFrozen string `json:"isFrozen"`
|
||||
Error string `json:"error"`
|
||||
Seq int64 `json:"seq"`
|
||||
}
|
||||
|
||||
// OrderbookItem holds data on an individual item
|
||||
type OrderbookItem struct {
|
||||
Price float64
|
||||
Amount float64
|
||||
Asks orderbook.LevelsArrayPriceAmount `json:"asks"`
|
||||
Bids orderbook.LevelsArrayPriceAmount `json:"bids"`
|
||||
IsFrozen string `json:"isFrozen"`
|
||||
Error string `json:"error"`
|
||||
Seq int64 `json:"seq"`
|
||||
}
|
||||
|
||||
// OrderbookAll contains the full range of orderbooks
|
||||
@@ -53,8 +48,8 @@ type OrderbookAll struct {
|
||||
|
||||
// Orderbook is a generic type golding orderbook information
|
||||
type Orderbook struct {
|
||||
Asks []OrderbookItem `json:"asks"`
|
||||
Bids []OrderbookItem `json:"bids"`
|
||||
Asks []orderbook.Level `json:"asks"`
|
||||
Bids []orderbook.Level `json:"bids"`
|
||||
}
|
||||
|
||||
// TradeHistory holds trade history data
|
||||
|
||||
Reference in New Issue
Block a user