github.com/bitfinexcom/bitfinex-api-go@v0.0.0-20210608095005-9e0b26f200fb/v2/websocket/api.go (about)

     1  package websocket
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/book"
     8  	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/common"
     9  	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/fundingcredit"
    10  	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/fundingloan"
    11  	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/fundingoffer"
    12  	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/order"
    13  )
    14  
    15  type FlagRequest struct {
    16  	Event string `json:"event"`
    17  	Flags int    `json:"flags"`
    18  }
    19  
    20  // API for end-users to interact with Bitfinex.
    21  
    22  // Send publishes a generic message to the Bitfinex API.
    23  func (c *Client) Send(ctx context.Context, msg interface{}) error {
    24  	socket, err := c.getSocket()
    25  	if err != nil {
    26  		return err
    27  	}
    28  	return socket.Asynchronous.Send(ctx, msg)
    29  }
    30  
    31  // Submit a request to enable the given flag
    32  func (c *Client) EnableFlag(ctx context.Context, flag int) (string, error) {
    33  	req := &FlagRequest{
    34  		Event: "conf",
    35  		Flags: flag,
    36  	}
    37  	// TODO enable flag on reconnect?
    38  	// create sublist to stop concurrent map read
    39  	socks := make([]*Socket, len(c.sockets))
    40  	c.mtx.RLock()
    41  	for i, socket := range c.sockets {
    42  		socks[i] = socket
    43  	}
    44  	c.mtx.RUnlock()
    45  	for _, socket := range socks {
    46  		err := socket.Asynchronous.Send(ctx, req)
    47  		if err != nil {
    48  			return "", err
    49  		}
    50  	}
    51  	return "", nil
    52  }
    53  
    54  // Gen the count of currently active websocket connections
    55  func (c *Client) ConnectionCount() int {
    56  	c.mtx.RLock()
    57  	defer c.mtx.RUnlock()
    58  	return len(c.sockets)
    59  }
    60  
    61  // Get the available capacity of the current
    62  // websocket connections
    63  func (c *Client) AvailableCapacity() int {
    64  	return c.getTotalAvailableSocketCapacity()
    65  }
    66  
    67  // Start a new websocket connection. This function is only exposed in case you want to
    68  // implicitly add new connections otherwise connection management is already handled for you.
    69  func (c *Client) StartNewConnection() error {
    70  	return c.connectSocket(SocketId(c.ConnectionCount()))
    71  }
    72  
    73  func (c *Client) subscribeBySocket(ctx context.Context, socket *Socket, req *SubscriptionRequest) (string, error) {
    74  	c.subscriptions.add(socket.Id, req)
    75  	err := socket.Asynchronous.Send(ctx, req)
    76  	if err != nil {
    77  		// propagate send error
    78  		return "", err
    79  	}
    80  	return req.SubID, nil
    81  }
    82  
    83  // Submit a request to subscribe to the given SubscriptionRequuest
    84  func (c *Client) Subscribe(ctx context.Context, req *SubscriptionRequest) (string, error) {
    85  	if c.getTotalAvailableSocketCapacity() <= 1 {
    86  		err := c.StartNewConnection()
    87  		if err != nil {
    88  			return "", err
    89  		}
    90  	}
    91  	// get socket with the highest available capacity
    92  	socket, err := c.getMostAvailableSocket()
    93  	if err != nil {
    94  		return "", err
    95  	}
    96  	return c.subscribeBySocket(ctx, socket, req)
    97  }
    98  
    99  // Submit a request to receive ticker updates
   100  func (c *Client) SubscribeTicker(ctx context.Context, symbol string) (string, error) {
   101  	req := &SubscriptionRequest{
   102  		SubID:   c.nonce.GetNonce(),
   103  		Event:   EventSubscribe,
   104  		Channel: ChanTicker,
   105  		Symbol:  symbol,
   106  	}
   107  	return c.Subscribe(ctx, req)
   108  }
   109  
   110  // Submit a request to receive trade updates
   111  func (c *Client) SubscribeTrades(ctx context.Context, symbol string) (string, error) {
   112  	req := &SubscriptionRequest{
   113  		SubID:   c.nonce.GetNonce(),
   114  		Event:   EventSubscribe,
   115  		Channel: ChanTrades,
   116  		Symbol:  symbol,
   117  	}
   118  	return c.Subscribe(ctx, req)
   119  }
   120  
   121  // Submit a  subscription request for market data for the given symbol, at the given frequency, with the given precision, returning no more than priceLevels price entries.
   122  // Default values are Precision0, Frequency0, and priceLevels=25.
   123  func (c *Client) SubscribeBook(ctx context.Context, symbol string, precision common.BookPrecision, frequency common.BookFrequency, priceLevel int) (string, error) {
   124  	if priceLevel < 0 {
   125  		return "", fmt.Errorf("negative price levels not supported: %d", priceLevel)
   126  	}
   127  	req := &SubscriptionRequest{
   128  		SubID:     c.nonce.GetNonce(),
   129  		Event:     EventSubscribe,
   130  		Channel:   ChanBook,
   131  		Symbol:    symbol,
   132  		Precision: string(precision),
   133  		Len:       fmt.Sprintf("%d", priceLevel), // needed for R0?
   134  	}
   135  	if !book.IsRawBook(string(precision)) {
   136  		req.Frequency = string(frequency)
   137  	}
   138  	return c.Subscribe(ctx, req)
   139  }
   140  
   141  // Submit a subscription request to receive candle updates
   142  func (c *Client) SubscribeCandles(ctx context.Context, symbol string, resolution common.CandleResolution) (string, error) {
   143  	req := &SubscriptionRequest{
   144  		SubID:   c.nonce.GetNonce(),
   145  		Event:   EventSubscribe,
   146  		Channel: ChanCandles,
   147  		Key:     fmt.Sprintf("trade:%s:%s", resolution, symbol),
   148  	}
   149  	return c.Subscribe(ctx, req)
   150  }
   151  
   152  // Submit a subscription request for status updates
   153  func (c *Client) SubscribeStatus(ctx context.Context, symbol string, sType common.StatusType) (string, error) {
   154  	req := &SubscriptionRequest{
   155  		SubID:   c.nonce.GetNonce(),
   156  		Event:   EventSubscribe,
   157  		Channel: ChanStatus,
   158  		Key:     fmt.Sprintf("%s:%s", string(sType), symbol),
   159  	}
   160  	return c.Subscribe(ctx, req)
   161  }
   162  
   163  // Retrieve the Orderbook for the given symbol which is managed locally.
   164  // This requires ManageOrderbook=True and an active chanel subscribed to the given
   165  // symbols orderbook
   166  func (c *Client) GetOrderbook(symbol string) (*Orderbook, error) {
   167  	c.mtx.RLock()
   168  	defer c.mtx.RUnlock()
   169  	if val, ok := c.orderbooks[symbol]; ok {
   170  		// take dereferenced copy of orderbook
   171  		return val, nil
   172  	}
   173  	return nil, fmt.Errorf("Orderbook %s does not exist", symbol)
   174  }
   175  
   176  // Submit a request to create a new order
   177  func (c *Client) SubmitOrder(ctx context.Context, onr *order.NewRequest) error {
   178  	socket, err := c.GetAuthenticatedSocket()
   179  	if err != nil {
   180  		return err
   181  	}
   182  	return socket.Asynchronous.Send(ctx, onr)
   183  }
   184  
   185  // Submit and update request to change an existing orders values
   186  func (c *Client) SubmitUpdateOrder(ctx context.Context, our *order.UpdateRequest) error {
   187  	socket, err := c.GetAuthenticatedSocket()
   188  	if err != nil {
   189  		return err
   190  	}
   191  	return socket.Asynchronous.Send(ctx, our)
   192  }
   193  
   194  // Submit a cancel request for an existing order
   195  func (c *Client) SubmitCancel(ctx context.Context, ocr *order.CancelRequest) error {
   196  	socket, err := c.GetAuthenticatedSocket()
   197  	if err != nil {
   198  		return err
   199  	}
   200  	return socket.Asynchronous.Send(ctx, ocr)
   201  }
   202  
   203  // Get a subscription request using a subscription ID
   204  func (c *Client) LookupSubscription(subID string) (*SubscriptionRequest, error) {
   205  	s, err := c.subscriptions.lookupBySubscriptionID(subID)
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  	return s.Request, nil
   210  }
   211  
   212  // Submit a new funding offer request
   213  func (c *Client) SubmitFundingOffer(ctx context.Context, fundingOffer *fundingoffer.SubmitRequest) error {
   214  	socket, err := c.GetAuthenticatedSocket()
   215  	if err != nil {
   216  		return err
   217  	}
   218  	return socket.Asynchronous.Send(ctx, fundingOffer)
   219  }
   220  
   221  // Submit a request to cancel and existing funding offer
   222  func (c *Client) SubmitFundingCancel(ctx context.Context, fundingOffer *fundingoffer.CancelRequest) error {
   223  	socket, err := c.GetAuthenticatedSocket()
   224  	if err != nil {
   225  		return err
   226  	}
   227  	return socket.Asynchronous.Send(ctx, fundingOffer)
   228  }
   229  
   230  // CloseFundingLoan - cancels funding loan by ID. Emits an error if not authenticated.
   231  func (c *Client) CloseFundingLoan(ctx context.Context, flcr *fundingloan.CancelRequest) error {
   232  	socket, err := c.GetAuthenticatedSocket()
   233  	if err != nil {
   234  		return err
   235  	}
   236  	return socket.Asynchronous.Send(ctx, flcr)
   237  }
   238  
   239  // CloseFundingCredit - cancels funding credit by ID. Emits an error if not authenticated.
   240  func (c *Client) CloseFundingCredit(ctx context.Context, fundingOffer *fundingcredit.CancelRequest) error {
   241  	socket, err := c.GetAuthenticatedSocket()
   242  	if err != nil {
   243  		return err
   244  	}
   245  	return socket.Asynchronous.Send(ctx, fundingOffer)
   246  }