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 }