shrimpy-dev / shrimpy-node

Shrimpy’s Developer Trading API is a unified way to integrating trading functionality across every major exchange. Collect historical market data, access real-time websockets, execute advanced trading strategies, and manage an unlimited number of users.
https://developers.shrimpy.io
MIT License
42 stars 19 forks source link

ShrimpyWsClient js code sample #4

Closed bitcoinvsalts closed 1 year ago

bitcoinvsalts commented 5 years ago

Hi guys,

A code sample in JS for how to connect to the Websocket using ShrimpyWsClient would be useful.

I am running the following code without success:

    let errorHandler = (error) => { console.log(error); };
    let client = new Shrimpy.ShrimpyWsClient(errorHandler);
    const subscribeData = {
        "type": "subscribe",
        "pair": "btc-usd",
        "exchange": "coinbasepro",
        "channel": "trade"
    };
    const unsubscribeData = {
        "type": "unsubscribe",
        "pair": "btc-usd",
        "exchange": "coinbasepro",
        "channel": "trade"
    };
    let handler = (msg) => { console.log(msg); };
    client.connect();
    client.subscribe(subscribeData, handler);
    client.unsubscribe(unsubscribeData);
    client.forceDisconnect();

Error log:

(node:49263) UnhandledPromiseRejectionWarning: Error: WebSocket is not open: readyState 0 (CONNECTING)
nnayudu commented 5 years ago

Hi jsappme, We are planning to make some improvements to the library and add some js code examples as well. Are there any other examples you would be interested in?

mijofields commented 4 years ago

I am also having issues establishing a JS websocket utilizing the example given. Rather than an error I get an undefined connection - when are you going to provide a working example?

jargote commented 4 years ago

The issue with the example is that client.connect() is an asynchronous call but the method does not return a promise, and the example does not explain you must wait for the socket connection to be stablished (wsClient.getReadyState() === 1) before you can subscribe to consume data.

This is the code to get around the issue.


const initWSClientAndSubscribe = () => {
        return this
          .createWSClient()
          .then((wsClient: ShrimpyWsClient) => {
            wsClient.connect()

            const waitUntilConnected = (): Promise<void> => {
              if (wsClient.getReadyState() === 1) {
                return Promise.resolve()
              }

              console.debug("waiting for socket connection ...")

              return sleep(1).then(() => waitUntilConnected())
            }

            return waitUntilConnected()
              .then(() => {
                tradingPairs.forEach((pair: ITradingPair) => {
                  const req: ISubscriptionRequest = createBBOSubscribeReq(Exchange.BINANCE, pair)

                  wsClient.subscribe(req, (data: IWebsocketMessage) => {
                    console.debug(data)
                  })
                })
              })
          })
          .catch(this.errorHandler)
}

const sleep = (seconds: number) => {
  return new Promise((resolve) => {
    setTimeout(() => resolve(), seconds * 1000)
  })
}
fvsgit commented 4 years ago

Hi Everyone,

After a few hours of frustration, I finally mannaged to get a basic sample of the WebSocket code working. As @jargote mentioned already, the connect() method of the websocket client actually does not return a promise. This means that the subscribe() method is immediately called after the connect when the connection has not been established yet.

After waiting for the connection to be established I ran into the second issue. The subscribeData object refers to the trading pair btc-usd. This is actually case sensitive and should be BTC-USD. Even the examples in the api documentaiton is lowercase and will not work.

Enough about the issues, more about the working code:

const Shrimpy = require('shrimpy-node'); 
let apiClient = null;
let wsClient = null;
let token = null; 

const publicKey = "<Public Key Here>";
const privateKey = "<Private Key Here>";

function handler(msg){
    console.log(msg);
};

function subscribeWhenConnected(oData){

    if (wsClient.getReadyState() === 1) {
        console.log("Subcribing to the order book for ETH-BTC");
        wsClient.subscribe(oData, handler); 
    } else {
        console.log("waiting for ws connection...");
        setTimeout(subscribeWhenConnected.bind(null, oData), 1000);
    }

};

function unsubscribe(oData){
    console.log("Unsubcribing now");
    wsClient.unsubscribe(oData);
    console.log("Stopping the application");
    process.exit(1);
};

(async () => {

    apiClient = new Shrimpy.ShrimpyApiClient(publicKey, privateKey);  
    token = await apiClient.getToken(); 
    wsClient = new Shrimpy.ShrimpyWsClient(function (error) {
        console.error(error);
    }, token);

    wsClient.connect(); 
    subscribeWhenConnected({
        "type": "subscribe",
        "pair": "ETH-BTC",
        "exchange": "binance",
        "channel": "orderbook"
    });  

    setTimeout(unsubscribe.bind(null, {
        "type": "unsubscribe",
        "pair": "ETH-BTC",
        "exchange": "binance",
        "channel": "orderbook"
    }), 10000); 

})();

NOTES:

  1. We use the public and private key with the ShrimpyApiClient to create a new instance of the API Client
  2. We then use the ShrimpyApiClient to generate a valid token that will be used for the ShrimpyWsClient. VERY IMPORTANT: the getToken() method returns a promise, so wait for it to finish. If you do not wait you will get an error that the token is invalid when trying to instantiate a new instance of the websocket client.
  3. With a valid token we create a new instance of the ShrimpyWsClient with an inline error callback.
  4. We then call the method wsClient.connect();
  5. Now the important part. Knowing that the connection will not be established yet, we call the function subscribeWhenConnected with the payload we would like to subscribe too.
  6. We then set a timeout for 10 seconds where we will then unsubscribe and stop the application.

subscribeWhenConnected This is a recursive function that will call itself every second to check if the connection state of the wsClient changed. Once the state is 1 the connection is ready and we can now successfully subscribe to the order book by calling the subscribe on the websocket client.

Hope this saves someone time in the future!