# Websocket

### Websocket Endpoints

#### Environments

There are two environments to access our websocket market data feeds:

1. **Live**

<pre><code><strong>wss://marketdata.aquanow.com/ 
</strong></code></pre>

2. **Sandbox**

```
wss://marketdata.staging.aquanow.com/
```

### Websocket Access and Authorization

API Authentication is done using an OAuth2.0. User needs to associate the following information in the payload of the subscription message. View the [Authorization](https://docs.aquanow.io/cams/security/oauth2.0) page for more detail.

#### Subscribe

In order to receive data feed, users sends a *subscribe* message to the server, after successful websocket connection. Users must also indicate the channel they wish to subscribe on in the subscription request.

\
When a subscribe message is received with a pair and channel, the server will respond with a **subscription** message that lists all channels you are subscribed to.

#### There are three channels you can subscribe for:

1. `orderBook` - Orderbook across various marketplaces and exchanges.
2. `ticker` - Open, High, Low, Close for a ticker, every minute published
3. `gbbo` - Real time best price/bids updates for subscribed ticker
4. `priceStatistics`  - 24 hour statistics for High, Low, % change in price and notional change for a pair over websocket channel. It is published every minute

**Note:** that the Subscribe request **MUST** be a JSON string. For example, in Javascript this can be achieved by using the `JSON.stringify` method.

#### Request Parameters

{% tabs %}
{% tab title="orderBook" %}

|       Name      |  Type  | Description                                                          | Required |
| :-------------: | :----: | -------------------------------------------------------------------- | -------- |
| `authorization` | String | Access token for OAuth authentication                                | Yes      |
|    `channel`    | String | Channel of subscription: orderBook                                   | Yes      |
|      `type`     | String | Type of action; subscribe or unsubscribe                             | Yes      |
|     `nonce`     | String | Numeric string timestamp in milliseconds of when the request is sent | Yes      |
|    `username`   | String | Username of a client in Aquanow System                               | Yes      |
|      `pair`     | String | Symbol/currency pair; ex. – pair: “BTC-USD”                          | Yes      |
|     `depth`     | Number | Depth of an orderbook; ex. – depth: 5                                | No       |
|   `precision`   | Number | <p>Precision level;<br>ex. - precision: 2</p>                        | No       |
|   {% endtab %}  |        |                                                                      |          |

{% tab title="ticker" %}

|       Name      |  Type  | Description                                                          | Required |
| :-------------: | :----: | -------------------------------------------------------------------- | -------- |
| `authorization` | String | Access token for Oauth authentication                                | Yes      |
|    `channel`    | String | Channel of subscription. Ex - "gbbo"                                 | Yes      |
|      `type`     | String | Type of action; subscribe or unsubscribe                             | Yes      |
|     `nonce`     | String | Numeric string timestamp in milliseconds of when the request is sent | Yes      |
|      `pair`     | String | Symbol/currency pair; ex. – pair: “BTC-CAD”                          | Yes      |
|    `username`   | String | Username of a client in Aquanow System                               | Yes      |
|   {% endtab %}  |        |                                                                      |          |

{% tab title="gbbo" %}

|       Name      |  Type  | Description                                                          | Required |
| :-------------: | :----: | -------------------------------------------------------------------- | -------- |
| `authorization` | String | Access token for Oauth authentication                                | Yes      |
|    `channel`    | String | Channel of subscription. Ex - "ticker"                               | Yes      |
|      `type`     | String | Type of action; subscribe or unsubscribe                             | Yes      |
|     `nonce`     | String | Numeric string timestamp in milliseconds of when the request is sent | Yes      |
|      `pair`     | String | Symbol/currency pair; ex. – pair: “BTC-CAD”                          | Yes      |
|    `username`   | String | Username of a client in Aquanow System                               | Yes      |
|   {% endtab %}  |        |                                                                      |          |

{% tab title="priceStatistics" %}

|       Name      |  Type  | Description                                                          | Required |
| :-------------: | :----: | -------------------------------------------------------------------- | -------- |
| `authorization` | String | Access token for Oauth authentication                                | Yes      |
|    `channel`    | String | Channel of subscription. Ex - "ticker"                               | Yes      |
|      `type`     | String | Type of action; subscribe or unsubscribe                             | Yes      |
|     `nonce`     | String | Numeric string timestamp in milliseconds of when the request is sent | Yes      |
|      `pair`     | String | Symbol/currency pair; ex. – pair: “BTC-CAD”                          | Yes      |
|    `username`   | String | Username of a client in Aquanow System                               | Yes      |
|   {% endtab %}  |        |                                                                      |          |
|  {% endtabs %}  |        |                                                                      |          |

#### Unsubscribe

You can unsubscribe from a channel at any time.

Example Request

{% tabs %}
{% tab title="Javascript" %}

```javascript
JSON.stringify({
  "type": "unsubscribe",      // Message Type
  "channel": 'orderBook',     // Subscription Channel
  "pair": 'BTC-USD'           // Symbol
})
```

{% endtab %}
{% endtabs %}

### Example Usage

{% tabs %}
{% tab title="orderBook" %}

```javascript
import axios from 'axios';
import qs from 'qs';
import WebSocket from 'ws';

const address = "wss://marketdata.aquanow.com";

const ws = new WebSocket(address);
const clientId = '';
const clientSecret = '';
const tokenEndpoint = `https://auth.cert.aquanow.com/oauth2/token`;
const scope = 'marketdata/read';

async function getAccessToken() {
  const authString = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
  const data = qs.stringify({
    grant_type: 'client_credentials',
    scope: scope,
  });

  try {
    const response = await axios.post(tokenEndpoint, data, {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        Authorization: `Basic ${authString}`,
      },
    });

    return response.data.access_token;
  } catch (error) {
    console.error('Error obtaining access token:', error.response.data);
  }
}

async function subscribeToOrders() {
  const authorization = await getAccessToken();
  const timestamp = Date.now();
  const subsOption = JSON.stringify({
    type: 'subscribe',
    channel: 'orderBook',
    authorization: 'Bearer XXXXXXXX',
    username: 'testAccount',
    pair: 'BTC-USD',
    nonce: timestamp,
    depth: 5
  });

  ws.send(subsOption);
}

ws.on('open', () => {
  console.log(`WS tunnel opened - trade`);
  subscribeToOrders();
});

ws.on('message', (data) => {
  console.log('data', JSON.parse(data));
});
```

{% endtab %}

{% tab title="ticker" %}

```javascript
import axios from 'axios';
import qs from 'qs';
import WebSocket from 'ws';

const address = "wss://marketdata.aquanow.com";

const ws = new WebSocket(address);
const clientId = '';
const clientSecret = '';
const tokenEndpoint = `https://auth.cert.aquanow.com/oauth2/token`;
const scope = 'marketdata/read';

async function getAccessToken() {
  const authString = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
  const data = qs.stringify({
    grant_type: 'client_credentials',
    scope: scope,
  });

  try {
    const response = await axios.post(tokenEndpoint, data, {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        Authorization: `Basic ${authString}`,
      },
    });

    return response.data.access_token;
  } catch (error) {
    console.error('Error obtaining access token:', error.response.data);
  }
}

async function subscribeToOrders() {
  const authorization = await getAccessToken();
  const timestamp = Date.now();
  const subsOption = JSON.stringify({
    type: 'subscribe',
    channel: 'gbbo',
    authorization: 'Bearer XXXXXXXX',
    username: 'testAccount',
    pair: 'BTC-USD',
    nonce: timestamp,
  });

  ws.send(subsOption);
}

ws.on('open', () => {
  console.log(`WS tunnel opened - trade`);
  subscribeToOrders();
});

ws.on('message', (data) => {
  console.log('data', JSON.parse(data));
});
```

{% endtab %}

{% tab title="gbbo" %}

```javascript
import axios from 'axios';
import qs from 'qs';
import WebSocket from 'ws';

const address = "wss://marketdata.aquanow.com";

const ws = new WebSocket(address);
const clientId = '';
const clientSecret = '';
const tokenEndpoint = `https://auth.cert.aquanow.com/oauth2/token`;
const scope = 'marketdata/read';

async function getAccessToken() {
  const authString = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
  const data = qs.stringify({
    grant_type: 'client_credentials',
    scope: scope,
  });

  try {
    const response = await axios.post(tokenEndpoint, data, {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        Authorization: `Basic ${authString}`,
      },
    });

    return response.data.access_token;
  } catch (error) {
    console.error('Error obtaining access token:', error.response.data);
  }
}

async function subscribeToOrders() {
  const authorization = await getAccessToken();
  const timestamp = Date.now();
  const subsOption = JSON.stringify({
    type: 'subscribe',
    channel: 'gbbo',
    authorization: 'Bearer XXXXXXXX',
    username: 'testAccount',
    pair: 'BTC-USD',
    nonce: timestamp,
  });

  ws.send(subsOption);
}

ws.on('open', () => {
  console.log(`WS tunnel opened - trade`);
  subscribeToOrders();
});

ws.on('message', (data) => {
  console.log('data', JSON.parse(data));
});
```

{% endtab %}

{% tab title="priceStatistics" %}

```javascript
import axios from 'axios';
import qs from 'qs';
import WebSocket from 'ws';

const address = "wss://marketdata.aquanow.com";

const ws = new WebSocket(address);
const clientId = '';
const clientSecret = '';
const tokenEndpoint = `https://auth.cert.aquanow.com/oauth2/token`;
const scope = 'marketdata/read';

async function getAccessToken() {
  const authString = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
  const data = qs.stringify({
    grant_type: 'client_credentials',
    scope: scope,
  });

  try {
    const response = await axios.post(tokenEndpoint, data, {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        Authorization: `Basic ${authString}`,
      },
    });

    return response.data.access_token;
  } catch (error) {
    console.error('Error obtaining access token:', error.response.data);
  }
}

async function subscribeToOrders() {
  const authorization = await getAccessToken();
  const timestamp = Date.now();
  const subsOption = JSON.stringify({
    type: 'subscribe',
    channel: 'priceStatistics',
    authorization: 'Bearer XXXXXXXX',
    username: 'testAccount',
    pair: 'BTC-USD',
    nonce: timestamp,
  });

  ws.send(subsOption);
}

ws.on('open', () => {
  console.log(`WS tunnel opened - trade`);
  subscribeToOrders();
});

ws.on('message', (data) => {
  console.log('data', JSON.parse(data));
});
```

{% endtab %}
{% endtabs %}

#### Example Responses

{% tabs %}
{% tab title="orderBook" %}

```json
{
  dataType: 'aggOB',
  lastUpdated: 1726790944059,
  symbol: 'BTC-USD',
  precision: [ -1, 0, 1, 2 ],
  bids: [
    { quote: 62708.59, quantity: 0.1, cumulativeQuantity: 0.1 },
    { quote: 62707.84, quantity: 0.2, cumulativeQuantity: 0.3 },
    { quote: 62707.39, quantity: 0.1, cumulativeQuantity: 0.4 },
    { quote: 62707.04, quantity: 0.3, cumulativeQuantity: 0.7 },
    { quote: 62706.84, quantity: 0.1, cumulativeQuantity: 0.8 }
  ],
  asks: [
    { quote: 63025.77, quantity: 0.1, cumulativeQuantity: 0.1 },
    { quote: 63026.65, quantity: 0.1, cumulativeQuantity: 0.2 },
    { quote: 63026.66, quantity: 0.1, cumulativeQuantity: 0.3 },
    { quote: 63028.97, quantity: 4, cumulativeQuantity: 4.3 },
    { quote: 63029.02, quantity: 0.1, cumulativeQuantity: 4.4 }
  ]
}
```

{% endtab %}

{% tab title="ticker" %}

```json
 {
  symbol: 'BTC-USD',
  exchange: 'Aqua',
  open: 62885,
  close: 62878.395,
  high: 62878.395,
  low: 62878.395,
  itemDateTime: 1726790880054
}
```

{% endtab %}

{% tab title="gbbo" %}

```json
{
  dataType: 'aggBBO',
  lastUpdated: 1726790834054,
  symbol: 'BTC-USD',
  precisions: [ -1, 0, 1, 2 ],
  bestBid: 62717.82,
  bestAsk: 63032.68,
  spread: 314.86
}

```

{% endtab %}

{% tab title="priceStatistics" %}

```json
{
  symbol: 'BTC-USD',
  itemDateTime: 1726790940000,
  high: 63871.645,
  low: 61585.59,
  percentChange: 1.4470937791356022,
  notionalChange: 897.12
}
```

{% endtab %}
{% endtabs %}
