# WS2 Migration Guide

Ethereal highly encourages all developers to upgrade their trading bots from the Socket.io gateway to our new, highly optimised WebSocket gateway.

We built the architecture from the ground up to provide a strictly superior trading and market data experience. By moving to native WebSockets, your integration will immediately benefit from:

* **Low latency**: Direct, raw WebSocket connections strip away the heavy Socket.io protocol overhead.
* **Higher throughput**: Tighten update intervals when streaming feeds.
* **Smaller payloads**: Minified JSON keys reduce bandwidth usage and parsing times.
* **Richer data:** Additional fields in certain streams that did not exist previously, including timestamp in all feeds.

If you are upgrading an existing trading bot, you will need to transition your connection logic, subscription payloads, and response parsers.

{% hint style="warning" %}
Both gateways will run side-by-side during the migration period. The Socket.IO gateway is deprecated and will be removed in a future release. Migrate to the native WebSocket gateway as soon as possible.
{% endhint %}

### Key Differences

Below summarises the breaking changes between the legacy Socket.IO gateway and the new native WebSocket gateway.

*Connection*

* **URL:** `wss://ws.ethereal.trade/v1/stream` to `wss://ws2.ethereal.trade/v1/stream`
* **No Socket.IO library:** use native `WebSocket` (browser) (or e.g. `ws` Node.js)
* **Max connection lifetime:** reduced from 12 hours to \~4 hours. Server closes with code `1000`, reason `"please reconnect"`. You must implement your own reconnection logic.

*Streams*

* **Renamed streams:** `BookDepth` renamed to `L2Book`, and `MarketPrice` renamed to `Ticker`. `Ticker` includes additional fields not available in `MarketPrice`.
* **New stream:** `PositionUpdate` (subaccount-based, WS2-only)
* **Market stream key:** `productId` (UUID) replaced by `symbol` (e.g., `"ETH-USD"`) for better usability

*Protocol*

* **Message format:** `socket.emit('subscribe', payload)` to `ws.send(JSON.stringify({ event: 'subscribe', data: payload }))`
* **Single message handler:** per-stream event listeners replaced by a single `onmessage` handler
* **Error format:** `{ status: 'BadRequest', error: '...' }` on `exception` event to `{ ok: false, code: 'VALIDATION_ERROR' }` inline

### Dry Run Execution

The legacy Socket.IO gateway supported dry run execution as a streaming event, allowing clients to subscribe and receive simulated order execution results in real time. **WS2 does not offer a dry run stream**. To perform dry run executions, you must use the HTTP API instead. For example, where you previously subscribed to a dry run event via `socket.emit('dryRunOrder', payload)`, you should now make a REST call such as `POST /v1/order/dry-run` with the same order payload in the request body.

The response will contain the simulated execution result, including estimated fills, fees, and slippage as a single JSON response rather than a stream of updates. There is no WebSocket equivalent for this functionality in WS2, so any workflows that depended on streaming dry run updates will need to be adapted to use synchronous HTTP requests.

### AI Assisted Migration

The absolute fastest way to migrate your existing bot is to use an AI agent IDE (Cursor) or CLI tools like Claude Code.

{% hint style="info" %}
Copy the prompt below and paste it into your agent's chat. It contains all the necessary architectural context and schema changes required to refactor your code.
{% endhint %}

<details>

<summary>AI Migration Prompt (Click to expand)</summary>

```
I need to upgrade my Ethereal Trade trading bot's WebSocket architecture from the `Socket.io (v1)` gateway to the new `Native WebSockets (v2)` gateway for lower latency and higher throughput.

Please rewrite this code to use the standard native WebSocket client library.

CRITICAL MIGRATION RULES & SCHEMA CHANGES:

1. Connection URL:
   - Old: `wss://ws.ethereal.trade/v1/stream` (socket.io)
   - New: `wss://ws2.ethereal.trade/v1/stream` (mainnet) or `wss://ws2.etherealtest.net/v1/stream` (testnet)
   - Adjust based on the environment in my code.

2. Connection Mechanics:
   - Remove all `socket.io-client` dependencies. Use a standard WebSocket client (e.g., `ws` in Node.js).
   - Implement automatic reconnection logic:
     - Server closes connections after ~4 hours with code 1000 and reason "please reconnect".
     - Idle connections with no message activity are closed after 2 minutes (server sends pings automatically, no client-side heartbeat needed).
     - All subscriptions are lost on disconnect.

3. Subscription Payload Changes:
   - `socket.io` `.emit()` is removed. All messages must be sent as `ws.send(JSON.stringify(payload))`.
   - Subscriptions must be wrapped in an `event` envelope:
     - Subscribe: `{ "event": "subscribe", "data": { "type": "<Channel>", ... } }`
     - Unsubscribe: `{ "event": "unsubscribe", "data": { "type": "<Channel>", ... } }`
   - Product channels now use `symbol` (e.g., "BTCUSD") instead of `productId` (UUID).
   - Subaccount channels still use `subaccountId` (UUID) — no change needed for these.

   Examples:
   - Old: `socket.emit("subscribe", { "type": "BookDepth", "productId": "<uuid>" })`
   - New: `ws.send(JSON.stringify({ "event": "subscribe", "data": { "type": "L2Book", "symbol": "BTCUSD" } }))`

4. Channel Name Changes:
   - `BookDepth` → `L2Book`
   - `MarketPrice` → `Ticker`
   - All other channels retain their names: `TradeFill`, `OrderUpdate`, `OrderFill`, `SubaccountLiquidation`, `TokenTransfer`.

5. Response Schema (Minified Keys):
   - There is no longer event-based routing from socket.io. Use a single `ws.on('message', ...)` handler and `JSON.parse()` the payload.
   - All data messages follow: `{ "e": "<EventName>", "t": <epoch_ms>, "data": { ... } }`
     - `e`: Event name (e.g., "L2Book", "Ticker"). Route logic based on this field.
     - `t`: Server timestamp in milliseconds.
     - `s` replaces `productId` inside data payloads (contains the symbol string).
   - Subscribe/unsubscribe acknowledgments: `{ "ok": true }` on success, `{ "ok": false, "code": "<error_code>" }` on failure.
   - Reference the full response schemas: https://docs.ethereal.trade/developer-guides/trading-api/websockets.md

6. Error Handling:
   - Errors are no longer emitted to a socket.io `exception` event.
   - Normal errors return: `{ "ok": false, "code": "<error_code>" }` (e.g., "UNKNOWN_PRODUCT", "VALIDATION_ERROR", "SUBSCRIPTION_LIMIT_EXCEEDED").
   - Rate limit errors return `{ "ok": false, "code": "RATE_LIMIT" }` and the connection is immediately closed. Handle this in your reconnect logic.
```

</details>
