WebSocket Rooms & Events

This guide will help you connect to Bluefin's WebSocket API, subscribe to event rooms, and listen to specific events in real-time using plain WebSockets or Bluefin TypeScript and Python client.

If connecting via plain WebSockets, the connection url is: wss://notifications.api.sui-prod.bluefin.io/

If connecting via Bluefin's TypeScript or Python clients, the connection is internally established via Socket.IO and comes out of the box with features for connection, reconnection and subscription.

Rooms & Events

There are two rooms a client can subscribe to, globalUpdates for all public information and userUpdates for private user information. Once subscribed, clients can listen to the following events in each room:

RoomEvent NameResponse
Global"MarketDataUpdate"See GET / marketData
Global"RecentTrades"See GET /recentTrades
Global"OrderbookUpdate"See Orderbook Diff. Depth Stream
OrderbookDepthStream"OrderbookDepthUpdate"See Orderbook Partial Depth Stream
Global"{symbol}@kline@{interval}"See /candlestickData
User"AccountDataUpdate"See /account
User"OrderUpdate"See /orders
User"OrderCancellationFailed"See /orders
User"PositionUpdate"See /userPosition
User"UserTrade"See /userTradesHistory
User"OrderSettlementUpdate"See OrderSettlementUpdate
User"OrderRequeueUpdate"See OrderRequeueUpdate
User"OrderCancelledOnReversionUpdate"See OrderCancelledOnReversionUpdate

Plain WebSockets

1. Open Connection

const WebSocket = require("ws");

const socketInstance: WebSocket = new WebSocket('wss://notifications.api.sui-prod.bluefin.io/');

socketInstance.onopen = function () {
        console.log('Connected to Bluefin WebSocket API');
};

socketInstance.onerror = function (err: any) {
        console.error('WebSocket Error:', err);
};
#install the `websocket-client`, `websockets` library beforehand: `# pip install websocket-client==1.5.1 websockets==9.1`

import websocket

websocket_url = "wss://notifications.api.sui-prod.bluefin.io/"

ws = websocket.create_connection(websocket_url)

print("WebSocket connection has been established: ", websocket_url)

2. Subscribe To Rooms

There are two rooms a client can connect to, globalUpdates for all public information and userUpdates for private user information.

Global Updates Room:

To subscribe to the globalUpdates room for a specific market, specify the symbol (e.g., "ETH-PERP"):

let globalSubscriptionMessage = [
  "SUBSCRIBE",
  [
    {
      e: "globalUpdates",
      p: "ETH-PERP", // You can replace "ETH-PERP" with any other valid market symbol
    },
  ],
];
socketInstance.send(JSON.stringify(globalSubscriptionMessage));
def subscribe_to_global_updates(ws):
  subscriptionMessage = [
    "SUBSCRIBE",
    [
      {
        "e": "globalUpdates",
        "p": "ETH-PERP", # You can replace "ETH-PERP" with any other valid market symbol
      }
    ]
  ]
  ws.send(json.dumps(subscriptionMessage))

User Updates Room:

Similarly, to subscribe to the userUpdates room for a specific user by specifying the auth token

let userSubscriptionMessage = [
  "SUBSCRIBE",
  [
    {
      e: "userUpdates",
      t: "YOUR_AUTH_TOKEN", // Replace with your actual token
    },
  ],
];
socketInstance.send(JSON.stringify(userSubscriptionMessage));
def subscribe_to_user_updates(ws):
  subscriptionMessage = [
    "SUBSCRIBE",
    [
      {
        "e": "userUpdates",
        "t": "YOUR_AUTH_TOKEN", # Replace with your actual token
      }
    ]
  ]
  ws.send(json.dumps(subscriptionMessage))

3. Listen To Events

Once subscribed you can begin listening to the room's events:

socketInstance.addEventListener('message', (event) => {
    const data = JSON.parse(event.data);

    switch (data.eventName) {
        case "MarketDataUpdate":
            // Handle MarketDataUpdate event
            break;
        case "RecentTrades":
            // Handle RecentTrades event
            break;
        case "OrderbookUpdate":
            break
        // Add cases for other events like OrderbookUpdate, MarketHealth, etc.
        default:
            break;
    }
});
import websocket
import json

def on_message(ws, message):
  
    data = json.loads(message)
    event_name = data["eventName"]
    
    if event_name == 'MarketDataUpdate':
        # Handle RecentTrades event
        pass
      
    elif event_name == 'RecentTrades':
        # Handle RecentTrades event
        pass
      
    elif event_name == 'OrderbookUpdate':
        # Handle OrderbookUpdate event
        pass
      
    else:
        # Add cases for other events like OrderbookUpdate, MarketHealth, etc.
        pass

while True:
  
    op_code, frame = ws.recv_data_frame(True)
    
    if op_code == websocket.ABNF.OPCODE_CLOSE:
        print("CLOSE frame received, closing websocket connection")
        
    elif op_code == websocket.ABNF.OPCODE_PING:
        ws.pong("")
        print("Received Ping; PONG frame sent back")
        
    elif op_code == websocket.ABNF.OPCODE_PONG:
        print("Received PONG frame")
        
    else:
        data = frame.data
        
        if op_code == websocket.ABNF.OPCODE_TEXT:
            data = data.decode("utf-8")
            
        on_message(ws, data)

4. Unsubscribe To Rooms

To unsubscribe to the room, just amend the type to be "UNSUBSCRIBE" like so for the Global Updates room:

let globalUnsubscriptionMessage = [
  "UNSUBSCRIBE",
  [
    {
      e: "globalUpdates",
      p: "ETH-PERP", // You can replace "ETH-PERP" with any other valid market symbol
    },
  ],
];
socketInstance.send(JSON.stringify(globalUnsubscriptionMessage));
def unsubscribe_to_global_updates(ws):
  unsubscriptionMessage = [
    "UNSUBSCRIBE",
    [
      {
        "e": "globalUpdates",
        "p": "ETH-PERP", # You can replace "ETH-PERP" with any other valid market symbol
      }
    ]
  ]
  ws.send(json.dumps(unsubscriptionMessage))

And User Updates Room:

let userUnsubscriptionMessage = [
  "UNSUBSCRIBE",
  [
    {
      e: "userUpdates",
      t: "YOUR_AUTH_TOKEN", // Replace with your actual token
    },
  ],
];
socketInstance.send(JSON.stringify(userUnsubscriptionMessage));
def unsubscribe_to_user_updates(ws):
  unsubscriptionMessage = [
    "UNSUBSCRIBE",
    [
      {
        "e": "userUpdates",
        "t": "YOUR_AUTH_TOKEN", # Replace with your actual token
      }
    ]
  ]
  ws.send(json.dumps(unsubscriptionMessage))

5. Running and Closing the Connection:

For Python, to establish and maintain the WebSocket connection:

while True:
  
    op_code, frame = ws.recv_data_frame(True)
    
    if op_code == websocket.ABNF.OPCODE_CLOSE:
        print("CLOSE frame received, closing websocket connection")
        
    elif op_code == websocket.ABNF.OPCODE_PING:
        ws.pong("")
        print("Received Ping; PONG frame sent back")
        
    elif op_code == websocket.ABNF.OPCODE_PONG:
        print("Received PONG frame")
        
    else:
        data = frame.data
        
        if op_code == websocket.ABNF.OPCODE_TEXT:
            data = data.decode("utf-8")
            
        on_message(ws, data)

Once you're done or if you wish to manually terminate the connection:

socketInstance.close();
ws.send_close()

Tying It All Together

const WebSocket = require("ws");

const socketInstance: WebSocket = new WebSocket('wss://notifications.api.sui-prod.bluefin.io/');

socketInstance.onopen = function () {
        console.log('Connected to Bluefin WebSocket API');
  
        let subscriptionMessage = [
            "SUBSCRIBE",[{ e: "globalUpdates", p: "ETH-PERP" }]
        ];
        socketInstance.send(JSON.stringify(subscriptionMessage));
};

socketInstance.onerror = function (err: any) {
        console.error('WebSocket Error:', err);
};

socketInstance.addEventListener('message', (event) => {
    const data = JSON.parse(event.data);

    switch (data.eventName) {
        case "MarketDataUpdate":
            // Handle MarketDataUpdate event
            break;
        case "RecentTrades":
            // Handle RecentTrades event
            break;
        case "OrderbookUpdate":
            break
        // Add cases for other events like OrderbookUpdate, MarketHealth, etc.
        default:
            break;
    }
});

#install the `websocket-client`, `websockets` library beforehand: `# pip install websocket-client==1.5.1 websockets==9.1`
import websocket
import json

def subscribe_to_global_updates(ws):
  subscriptionMessage = [
    "SUBSCRIBE",
    [
      {
        "e": "globalUpdates",
        "p": "ETH-PERP", # You can replace "ETH-PERP" with any other valid market symbol
      }
    ]
  ]
  ws.send(json.dumps(subscriptionMessage))

def on_message(ws, message):
  
    data = json.loads(message)
    
    event_name = data["eventName"]
    
    if event_name == 'MarketDataUpdate':
        # Handle RecentTrades event
        pass
      
    elif event_name == 'RecentTrades':
        # Handle RecentTrades event
        pass
      
    elif event_name == 'OrderbookUpdate':
        # Handle OrderbookUpdate event
        pass
      
    else:
        # Add cases for other events like OrderbookUpdate, MarketHealth, etc.
        pass
      
      
websocket_url = "wss://notifications.api.arbitrum-prod.firefly.exchange/"

ws = websocket.create_connection(websocket_url)

print("WebSocket connection has been established: ", websocket_url)

subscribe_to_global_updates(ws)

while True:
  
    op_code, frame = ws.recv_data_frame(True)
    
    if op_code == websocket.ABNF.OPCODE_CLOSE:
        print("CLOSE frame received, closing websocket connection")
        
    elif op_code == websocket.ABNF.OPCODE_PING:
        ws.pong("")
        print("Received Ping; PONG frame sent back")
        
    elif op_code == websocket.ABNF.OPCODE_PONG:
        print("Received PONG frame")
        
    else:
        data = frame.data
        
        if op_code == websocket.ABNF.OPCODE_TEXT:
            data = data.decode("utf-8")
            
        on_message(ws, data)

Bluefin Client Library

1. Create a connection_callback function that encapsulates the following:

  • Subscribe to the global market room. If the user wishes to listen to global events, then use
client.sockets.subscribeGlobalUpdatesBySymbol(marketSymbol)
# subscribe to global event updates for ETH market
status = await client.socket.subscribe_global_updates_by_symbol(MARKET_SYMBOLS.ETH)
  • Subscribe to the orderbook partial depth events room. If the user wishes to listen to orderbook depth stream events, then use
client.sockets.subscribeOrderBookDepthStreamBySymbol(marketSymbol,depth)

or 

client.sockets.subscribeOrderBookDepthStreamBySymbol(marketSymbol) //With default depth being 5
# subscribe to orderbook partial depth event
status = await client.socket.subscribe_orderbook_depth_streams_by_symbol(MARKET_SYMBOLS.ETH,depth)

or 

status = await client.socket.subscribe_orderbook_depth_streams_by_symbol(MARKET_SYMBOLS.ETH) # With default depth being 5
  • Both of these methods must be invoked separately for every market.
  • Subscribe to the local user events room. If you wish to listen to events pertaining to your account, use
client.sockets.subscribeUserUpdateByToken()
# subscribe to local user events
status = await client.socket.subscribe_user_update_by_token()
  • You can write a callback function and pass it as an argument to any of the event listeners like this:
  const callback = ({ order }: { order: PlaceOrderResponse }) => {
      console.log(order);
  };

  client.sockets.onUserOrderUpdate(callback);
def callback(event):
        global event_received
        print(event)
        event_received = True
        
# triggered when user order updates are received
await client.socket.listen(SOCKET_EVENTS.ORDER_UPDATE.value, callback)
  1. Add a connection_callback to the listener for the connect event
await client.sockets.listen("connect", connection_callback)
await client.socket.listen("connect", connection_callback)
  • This function will be executed when a socket connection is established.
  • 💡

    The connect event also gets triggered when the socket client attempts auto-reconnection; therefore, it is important to add the Subscribe calls to this callback function to avoid manual intervention when the connection re-establishes.

3. You can also choose to perform any action when the socket client disconnects from the server by adding a disconnection_callback to the listener for.disconnect event

await client.sockets.listen("disconnect", disconnection_callback)
await client.socket.listen("disconnect", disconnection_callback)

4. After defining the listener for the connection event using the connection_callback function, open up a socket connection

client.sockets.open()
await client.socket.open()

5. Terminate the connection once done

client.sockets.close()
await client.socket.close()

Tying It All Together

/**
 * Places a market order on exchange and listens to emitted events
 */

/* eslint-disable no-console */
import {
  BluefinClient,
  MARKET_SYMBOLS, 
  Networks, 
  ORDER_SIDE, 
  ORDER_TYPE, 
  PlaceOrderResponse
} from "@bluefin-exchange/bluefin-v2-client";

async function main() {
  const ed25519Wallet =
    "trigger swim reunion gate hen black real deer light nature trial dust";
  const client = new BluefinClient(
    true,
    Networks.TESTNET_SUI,
    ed25519Wallet,
    "ED25519"
  );
  await client.init();

  const callback = ({ order }: { order: PlaceOrderResponse }) => {
    console.log(order);

    // kill sockets in order to stop script
    client.sockets.close();
  };

  const connection_callback = async () => {
    // This callback will be invoked as soon as the socket connection is established
    // start listening to global market and local user events
    client.sockets.subscribeGlobalUpdatesBySymbol(MARKET_SYMBOLS.ETH);
    client.sockets.subscribeUserUpdateByToken();

    // triggered when order updates are received
    client.sockets.onUserOrderUpdate(callback);

  };

  const disconnection_callback = async () => {
    console.log("Sockets disconnected, performing actions...")
    const resp = await client.cancelAllOpenOrders(MARKET_SYMBOLS.ETH);
    console.log(resp);
  };

  // must specify connection_callback before opening the sockets below
  await client.sockets.listen("connect", connection_callback);
  await client.sockets.listen("disconnect", disconnection_callback);

  console.log("Making socket connection to firefly exchange");
  client.sockets.open();

  /******  Placing an Order ******/
  // default leverage of account is set to 3 on Bluefin
  const leverage = await client.getUserDefaultLeverage(MARKET_SYMBOLS.ETH)

  // post order
  const response = await client.postOrder({
    symbol: symbol,
    price: 50,
    quantity: 0.5,
    side: ORDER_SIDE.BUY,
    orderType: ORDER_TYPE.LIMIT,
    leverage: leverage,
  });

  console.log(response.data);
}

main().then().catch(console.warn);
import time
from config import TEST_ACCT_KEY, TEST_NETWORK
from bluefin_v2_client import BluefinClient, Networks, MARKET_SYMBOLS, SOCKET_EVENTS
import asyncio

event_received = False


def callback(event):
    global event_received
    print("Event data:", event)
    event_received = True


async def main():
    client = BluefinClient(True, Networks[TEST_NETWORK], TEST_ACCT_KEY)
    await client.init(True)
    response = await client.generate_readonly_token()
    readOnlyclient = BluefinClient(True, Networks[TEST_NETWORK])
    await readOnlyclient.init(True, response)

    async def my_callback():
        print("Subscribing To Rooms")
        # subscribe to global event updates for BTC market
        status = await readOnlyclient.socket.subscribe_global_updates_by_symbol(
            MARKET_SYMBOLS.BTC
        )
        print("Subscribed to global BTC events: {}".format(status))

        # subscribe to local user events
        status = await readOnlyclient.socket.subscribe_user_update_by_token()
        print("Subscribed to user events: {}".format(status))

        # triggered when order book updates
        print("Listening to exchange health updates")
        await readOnlyclient.socket.listen(
            SOCKET_EVENTS.EXCHANGE_HEALTH.value, callback
        )

        # triggered when status of any user order updates
        print("Listening to user order updates")
        await readOnlyclient.socket.listen(SOCKET_EVENTS.ORDER_UPDATE.value, callback)

    await readOnlyclient.socket.listen("connect", my_callback)

    # must open socket before subscribing
    print("Making socket connection to Bluefin exchange")
    await readOnlyclient.socket.open()

    # SOCKET_EVENTS contains all events that can be listened to

    # logs event name and data for all markets and users that are subscribed.
    # helpful for debugging
    # client.socket.listen("default",callback)
    timeout = 30
    end_time = time.time() + timeout
    while not event_received and time.time() < end_time:
        time.sleep(1)

    # # unsubscribe from global events
    status = await readOnlyclient.socket.unsubscribe_global_updates_by_symbol(
        MARKET_SYMBOLS.BTC
    )
    print("Unsubscribed from global BTC events: {}".format(status))

    status = await readOnlyclient.socket.unsubscribe_user_update_by_token()
    print("Unsubscribed from user events: {}".format(status))

    # # close socket connection
    print("Closing sockets!")
    await readOnlyclient.socket.close()

    await readOnlyclient.apis.close_session()


if __name__ == "__main__":
    loop = asyncio.new_event_loop()
    loop.run_until_complete(main())
    loop.close()