> For the complete documentation index, see [llms.txt](https://docs.hydromancer.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hydromancer.xyz/readme/websocket/trades.md).

# trades

HL-compatible per-coin trade stream. Each message contains a batch of `WsTrade` objects synthesized from block fills — one per matched (buyer, seller) pair.

Per-coin `trades` is included in every tier (5/25/100 coins for starter/growth/scale). Streaming **all** coins with no `coins` filter is an add-on and requires the `ws:allTrades` permission.

### Subscribe

```json
{
    "type": "subscribe",
    "subscription": {
        "type": "trades",
        "coin": "BTC",                  // optional, single-coin (HL-native shape)
        "coins": ["xyz:SP500", "BTC"]   // optional, multi-coin; omit both for the all-coins firehose
    }
}
```

| Parameter | Type      | Description                                                                                                                                                                            |
| --------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `coin`    | string    | Single-coin filter (HL's native wire shape, accepted for compatibility). Full coin string including any prefix, e.g. `"BTC"`, `"xyz:SP500"`, `"#90"`, `"@107"` / `"PURR/USDC"` (spot). |
| `coins`   | string\[] | Multi-coin filter (case-sensitive, up to the tier's `max_coins`: 5/25/100). Same coin format as `coin`.                                                                                |

`coin` and `coins` may be combined — their union is the effective coin set. Omitting both makes this the all-coins firehose, which requires the `ws:allTrades` add-on permission.

{% hint style="info" %}
This is wire-compatible with Hyperliquid: HL clients sending the single `coin: "BTC"` field work as-is. The `coins` array is a Hyna extension for subscribing to several coins in one subscription.
{% endhint %}

{% hint style="warning" %}
**One trades subscription per connection.** All `trades` messages share a single `trades` channel with no per-subscription identity, so the client cannot tell two trades streams apart. A connection may hold at most one trades subscription (per-coin **or** firehose). To watch several coins, list them all in one subscription's `coins` array rather than opening multiple subscriptions. A second, distinct trades subscribe on the same connection is rejected with an error; unsubscribe first to switch between a per-coin filter and the firehose. Re-sending the identical subscription is a no-op. Separate connections each get their own trades subscription. The coin cap (5/25/100) is counted cumulatively across the API key.
{% endhint %}

### Unsubscribe

```json
{
    "type": "unsubscribe",
    "subscription": {
        "type": "trades",
        "coins": ["BTC"]
    }
}
```

### Trade data format

The response matches HL's `WsTrade` shape exactly. Trades are batched per block, ordered by the underlying fill order — ascending `(time, tx_index)`, so the earliest fill in the block appears first.

```json
{
  "type": "trades",
  "seq": 1,
  "cursor": "500:1704067200000:0",
  "trades": [
    {
      "coin": "BTC",
      "side": "B",
      "px": "62541.0",
      "sz": "0.0006",
      "hash": "0xeca4564b44c8a8e5ee1e043e5cac07020fa10030dfcbc7b7906d019e03cc82d0",
      "time": 1782203279565,
      "tid": 414334974001319,
      "users": [
        "0xf83fc34248744a304872e1ed40b9b10f54a64f6f",
        "0x2ca4927174ba283d8a57f60ef3589844035a2930"
      ]
    }
  ]
}
```

**Field semantics:**

| Field   | Description                                                                                                                                         |
| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `coin`  | Coin identifier (matches `coins` filter format).                                                                                                    |
| `side`  | The **taker's** side. `"B"` = taker bought, `"A"` = taker sold.                                                                                     |
| `px`    | Trade price.                                                                                                                                        |
| `sz`    | Trade size.                                                                                                                                         |
| `hash`  | Fill hash.                                                                                                                                          |
| `time`  | Trade timestamp (ms).                                                                                                                               |
| `tid`   | Trade id. Unique within `(coin, time)`; for a globally unique id use `(block_time, coin, tid)`.                                                     |
| `users` | `[buyer, seller]` — matches Hyperliquid. The taker is `users[0]` when `side="B"` and `users[1]` when `side="A"` (use `side` to identify the taker). |

#### Common errors

```
Subscription allTrades requires permission: ws:allTrades
```

(Only the no-filter firehose form requires the add-on; per-coin `trades` is included in every tier.)

### Examples

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

```javascript
const WebSocket = require('ws');

const ws = new WebSocket(`wss://api.hydromancer.xyz/ws?token=${process.env.HYDROMANCER_API_KEY}`);

ws.on('message', (data) => {
    const msg = JSON.parse(data);

    if (msg.type === 'connected') {
        ws.send(JSON.stringify({
            type: 'subscribe',
            subscription: { type: 'trades', coins: ['BTC', 'ETH'] }
        }));
    } else if (msg.type === 'ping') {
        ws.send(JSON.stringify({ type: 'pong' }));
    } else if (msg.type === 'trades') {
        for (const t of msg.trades) {
            const [buyer, seller] = t.users;
            console.log(`${t.coin} ${t.side} ${t.sz}@${t.px}  buyer=${buyer} seller=${seller}`);
        }
    }
});
```

{% endtab %}

{% tab title="Python" %}

```python
import websocket, json, os

def on_message(ws, message):
    msg = json.loads(message)
    if msg['type'] == 'connected':
        ws.send(json.dumps({
            "type": "subscribe",
            "subscription": {"type": "trades", "coins": ["BTC", "ETH"]}
        }))
    elif msg['type'] == 'ping':
        ws.send(json.dumps({'type': 'pong'}))
    elif msg['type'] == 'trades':
        for t in msg['trades']:
            buyer, seller = t['users']
            print(f"{t['coin']} {t['side']} {t['sz']}@{t['px']}  buyer={buyer} seller={seller}")

ws = websocket.WebSocketApp(
    f"wss://api.hydromancer.xyz/ws?token={os.environ.get('HYDROMANCER_API_KEY')}",
    on_message=on_message
)
ws.run_forever()
```

{% endtab %}
{% endtabs %}
