Stage 2: Websocket Management

Websocket connections are essential for real-time data streaming and order management in the Tradovate API. This stage validates your integration’s ability to maintain stable websocket connections and handle real-time events.

Overview

The websocket management stage ensures your application can:

  • Establish and maintain websocket connections
  • Handle authentication over websocket
  • Process real-time events
  • Manage connection heartbeats and reconnection logic
  • Handle rate limiting and synchronous requests

Required Tests

1. Websocket Login

Purpose: Authenticate websocket connection using access token.

Test Steps:

  1. Establish websocket connection to wss://demo.tradovateapi.com/v1/websocket
  2. Send authentication message with valid access token
  3. Verify successful authentication response
  4. Test with invalid/expired token to ensure proper error handling

Example Implementation:

const ws = new WebSocket('wss://demo.tradovateapi.com/v1/websocket');
ws.onopen = function() {
// Send authentication message
const authMessage = `authorize\n0\n\n${accessToken}`;
ws.send(authMessage);
};
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
if (data.i === 0) {
// Authentication response
console.log('Authentication successful:', data);
}
};

Expected Authentication Response:

{
"i": 0,
"s": 200
}

Expected Error Response:

{
"i": 0,
"s": 401
}

Validation Criteria:

  • ✅ Websocket connection establishes successfully
  • ✅ Authentication succeeds with valid token
  • ✅ Authentication fails with invalid token
  • ✅ Connection remains stable after authentication

2. Heartbeats

Purpose: Maintain connection health and detect disconnections.

Test Steps:

  1. Send periodic heartbeat messages
  2. Verify heartbeat responses are received
  3. Test heartbeat timeout scenarios
  4. Verify connection recovery after missed heartbeats

Example Implementation:

// Track last message time
let lastMessageTime = new Date().getTime();
ws.onmessage = function(event) {
const timestamp = new Date().getTime();
if(timestamp - lastMessageTime > 2500) {
ws.send('[]'); // Empty frame is a heartbeat in our protocol
lastMessageTime = timestamp; // Update last message time
}
// carry on with message handling
// ...
};

Validation Criteria:

  • ✅ Heartbeats are sent at regular intervals
  • ✅ Heartbeat mechanism works during high message volume

3. Event Handling

Purpose: Process real-time events from the websocket.

Test Steps:

  1. Manage real-time subscriptions
    • Start event streaming using user/syncrequest
    • subscribe to relevant entity types using the entityTypes parameter
  2. Process incoming events

Example user/syncrequest:

// Connect to the websocket
const ws = new WebSocket('wss://demo.tradovateapi.com/v1/websocket');
const requestBody = {
splitResponses: true,
// Optional, define sharding expression
shardingExpression: {
expressionType: 'modUserId', // | 'modAccountId'
divisor: 1, // each instance should have the same divisor
remainder: 0, // modulus of the ID of user or account to process on this instance
},
entityTypes: [
'account',
'accountRiskStatus',
'cashBalance',
'commandReport',
'command',
'executionReport',
'fill',
'fillPair',
'order',
'orderStrategy',
'position',
'product',
'user'
],
}
ws.onopen = function() {
ws.send(`authorize\n0\n\n${accessToken}`);
}
ws.onmessage = function(event) {
const type = event.data.slice(0, 1);
let data = null;
if(type === 'a') {
data = JSON.parse(event.data.slice(1));
}
if(data.i === 0) {
// Authentication response
console.log('Authentication successful:', data);
// Send syncrequest
ws.send(`user/syncrequest\n1\n\n${JSON.stringify(requestBody)}`);
}
}
ws.onerror = function(event) {
console.error('WebSocket error:', event);
}

Validation Criteria:

  • ✅ Only one syncrequest is sent per socket lifecycle.
  • ✅ Real-time events are received
  • ✅ Sharding is implemented correctly
  • ✅ Entity types are subscribed to correctly
  • ✅ Syncrequest is sent correctly
  • ✅ Event handlers are robust and don’t crash on malformed data

4. Rate Limit Handling (P-Ticket)

Purpose: Handle rate limiting and priority ticket system.

For what penalty tickets are, what triggers them, and how to recover, see Penalty Tickets.

Test Steps:

  1. Send requests that trigger rate limiting
  2. Verify P-Ticket system responses
  3. Implement proper queuing and retry logic
  4. Test priority handling for different request types

Example Implementation:

const send = requestData => {
return new Promise((res, rej) => {
let i = ++lastId;
let race = setTimeout(() => { reject(new Error('Request timeout'))}, 30000);
function handler(event) {
const type = event.data.slice(0, 1);
if(type === 'a') {
const data = JSON.parse(event.data.slice(1));
// data is always an array
for(const datum of data) {
if(datum.i === i) {
if(datum.d && 'p-ticket' in datum.d) {
const pTicket = datum.d['p-ticket'];
const pTime = datum.d['p-time'];
const pCaptcha = datum.d['p-captcha'];
const pMessage = datum.d['p-message'];
console.log(`Rate limit reason: ${pMessage}`);
if(pCaptcha) {
// Handle captcha, if you receive it you have been locked out for 1 hour
console.error('p-captcha received. Operations locked for 1 hour.');
return;
} else {
// Handle rate limiting with P-Ticket, resend the request
// with the p-ticket after p-time seconds.
setTimeout(() => {
send({
...requestData,
body: { ...requestData.body, 'p-ticket': pTicket }
});
}, pTime * 1000);
ws.removeEventListener('message', handler);
}
} else {
clearTimeout(race);
res(datum.d);
ws.removeEventListener('message', handler);
}
}
}
}
}
// construct the message string
const queryString = requestData.query || ''
const bodyString = JSON.stringify(requestData.body || '')
const messageString = `${requestData.endpoint}\n${i}\n${queryString}\n${bodyString}`;
ws.addEventListener('message', handler);
ws.send(messageString);
});
}

Validation Criteria:

  • ✅ Rate limiting responses are properly handled
  • ✅ P-Ticket system is implemented correctly

5. Reconnection

Purpose: Automatically reconnect after connection loss.

Test Steps:

  1. Simulate connection drops
  2. Verify automatic reconnection logic
  3. Test re-authentication after reconnection
  4. Verify state recovery after reconnection

Example Implementation:

let lastMessageTime = new Date().getTime();
ws.onmessage = async function(event) {
const timestamp = new Date().getTime();
if(timestamp - lastMessageTime > 15000) {
// connection has been idle for 15 seconds, reconnect
await reconnect();
}
lastMessageTime = timestamp;
}

Validation Criteria:

  • ✅ Automatic reconnection works after connection loss
  • ✅ Re-authentication occurs after reconnection
  • ✅ State is properly recovered after reconnection

Performance Requirements

  • Heartbeat: Send heartbeats every 2.5 seconds (2,500 ms)

For detailed performance requirements, contact Evaluation Support.

Testing Checklist

  • Websocket connection establishes successfully
  • Authentication works with valid tokens
  • Heartbeats are sent and received
  • Events are processed correctly
  • Rate limiting is handled properly
  • Syncrequests work as expected and are called only once per socket lifecycle
  • Reconnection works automatically

Next Steps

After completing Stage 2 websocket management tests, proceed to Stage 3: User Management.