> 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/candles.md).

# candles

{% hint style="info" %}

### Currently we offer 1s, 15s, and 30s candlesticks which are not available on Hyperliquid natively. Our candlesticks endpoint also return more than 5k last candles available through the native Hyperliquid API.

{% endhint %}

Streams real-time candle updates as trades happen. Each message contains the latest state of the current candle for the subscribed coin and interval. When a candle closes and a new one opens, both events are sent.

### Subscribe

```json
{
    "method": "subscribe",
    "subscription": {
        "type": "candle",
        "coin": "BTC",
        "interval": "1m"
    }
}
```

| Field      | Type   | Required | Description                                                                                                                |
| ---------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------- |
| `coin`     | string | Yes      | Coin name (e.g. `"BTC"`, `"ETH"`, `"hyna:ETH"`)                                                                            |
| `interval` | string | Yes      | Candle interval: `1s`, `15s`, `30s`, `1m`, `3m`, `5m`, `15m`, `30m`, `1h`, `2h`, `4h`, `8h`, `12h`, `1d`, `3d`, `1w`, `1M` |

### Unsubscribe

```json
{
    "method": "unsubscribe",
    "subscription": {
        "type": "candle",
        "coin": "BTC",
        "interval": "1m"
    }
}
```

### Update data format

Each message contains a full candle snapshot. The `x` field indicates whether the candle is finalized (`true`) or still accumulating trades (`false`).

```json
{
    "type": "candle",
    "seq": 42,
    "cursor": "1700000040000",
    "data": {
        "s": "BTC",
        "i": "1m",
        "t": 1700000040000,
        "T": 1700000099999,
        "o": "45000.00",
        "h": "45100.50",
        "l": "44980.00",
        "c": "45050.25",
        "v": "123.45",
        "q": "5560125.00",
        "n": 312,
        "x": false
    }
}
```

### Field definitions

**Envelope fields:**

| Field    | Type   | Description                                                            |
| -------- | ------ | ---------------------------------------------------------------------- |
| `type`   | string | Always `"candle"`                                                      |
| `seq`    | number | Per-subscription sequence number (starts at 1, increments per message) |
| `cursor` | string | Candle open time as millisecond timestamp string (for session replay)  |

**Candle fields (`data`):**

| Field | Type    | Description                                                     |
| ----- | ------- | --------------------------------------------------------------- |
| `s`   | string  | Coin name                                                       |
| `i`   | string  | Interval string                                                 |
| `t`   | number  | Candle open time (milliseconds, interval start)                 |
| `T`   | number  | Candle close time (milliseconds, interval end)                  |
| `o`   | string  | Open price                                                      |
| `h`   | string  | High price                                                      |
| `l`   | string  | Low price                                                       |
| `c`   | string  | Close (latest) price                                            |
| `v`   | string  | Volume in base asset                                            |
| `q`   | string  | Quote volume (notional value)                                   |
| `n`   | number  | Number of trades                                                |
| `x`   | boolean | `true` if the candle is closed/finalized, `false` if still open |

### 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({
            method: 'subscribe',
            subscription: {
                type: 'candle',
                coin: 'BTC',
                interval: '1m'
            }
        }));
    } else if (msg.type === 'ping') {
        ws.send(JSON.stringify({ type: 'pong' }));
    } else if (msg.type === 'candle') {
        const c = msg.data;
        const status = c.x ? 'CLOSED' : 'open';
        console.log(`[${status}] ${c.s} ${c.i}  O=${c.o} H=${c.h} L=${c.l} C=${c.c} V=${c.v} trades=${c.n}`);
    }
});
```

{% endtab %}

{% tab title="Python" %}

```python
import websocket
import json
import os

def on_message(ws, message):
    msg = json.loads(message)

    if msg['type'] == 'connected':
        ws.send(json.dumps({
            "method": "subscribe",
            "subscription": {
                "type": "candle",
                "coin": "BTC",
                "interval": "1m"
            }
        }))
    elif msg['type'] == 'ping':
        ws.send(json.dumps({'type': 'pong'}))
    elif msg['type'] == 'candle':
        c = msg['data']
        status = 'CLOSED' if c['x'] else 'open'
        print(f"[{status}] {c['s']} {c['i']}  O={c['o']} H={c['h']} L={c['l']} C={c['c']} V={c['v']} trades={c['n']}")

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 %}

### Common errors

| Error                | Cause                                                           |
| -------------------- | --------------------------------------------------------------- |
| `Invalid interval`   | Interval string not in the valid list                           |
| `Invalid coin`       | Coin contains invalid characters or exceeds max length          |
| `Connection timeout` | Not responding to ping messages — reply with `{"type": "pong"}` |
