<!-- https://zunderlabs.com/docs/integrations/tradingview · Markdown version of the page -->

# TradingView alerts through the relay

Turn TradingView alerts into guarded Hyperliquid orders. Why a relay is needed, the alert format, and what the relay can and cannot do.

:::note[Planned]
The relay and the alert format are planned. Nothing on this page works yet. TradingView's requirements below are from [TradingView, "About webhooks"](https://www.tradingview.com/support/solutions/43000529348-about-webhooks/), read 6 Oct 2026.
:::

## Why a relay

TradingView sends a webhook as an HTTP POST from its servers. Its rules:

- only ports **80 and 443** are accepted;
- the server must answer within **three seconds**, or the request is cancelled;
- no IPv6;
- webhooks need two-factor authentication on the TradingView account;
- webhooks "may occasionally fail" to arrive.

Guard listens on `127.0.0.1:8547`, on your machine. TradingView cannot reach it, and you should not open your machine to the internet to change that. So alerts go to a relay instead.

## How it works

```text
TradingView ──HTTPS POST──▶ Zunder Relay ══WebSocket (opened by your Guard)══▶ your Guard ──▶ Hyperliquid
            your personal URL              forwards the alert text only       nine rules, signs
```

1. Your Guard opens an **outbound** WebSocket to the relay. You open no port.
2. You paste your personal relay URL into the TradingView alert's webhook field.
3. TradingView posts the alert message to the relay. The relay forwards the text to your Guard and answers TradingView at once.
4. Your Guard parses the alert, applies the nine rules, signs with your API wallet and sends to Hyperliquid.

The relay holds no key and no funds and cannot place an order. Details: [The relay](https://zunderlabs.com/docs/tools/relay).

## The alert message

```json
{
  "zunder": 1,
  "secret": "your-alert-secret",
  "coin": "{{ticker}}",
  "side": "buy",
  "stop": 58800,
  "price": {{close}}
}
```

| Field | Meaning |
|---|---|
| `zunder` | format version |
| `secret` | a passphrase only your Guard knows; an alert without it is dropped |
| `coin` | Hyperliquid coin name, for example `BTC`. `{{ticker}}` works only if your chart's ticker matches |
| `side` | `buy`, `sell` or `close` |
| `stop` | the stop price. Guard sizes from it; there is no size field |
| `price` | the reference price; Guard bounds the order's worst fill around it |

:::caution[Draft]
The field names are a draft and will change before release. Two design points are open: how the alert is authenticated (TradingView cannot sign a request, so the relay URL and the secret are bearer secrets: anyone who has both can send alerts, which still pass your rules), and how replayed alerts are refused. See [Threat model](https://zunderlabs.com/docs/security/threat-model).
:::

**Example.** A breakout alert fires on BTC with `"stop": 58800` at a close of 60,000. Guard sizes the entry so that the stop loses at most 2% of equity, places the entry and its stop together, and records the decision. If the daily loss stop has fired, the alert is refused and the refusal is in your Guard's log, not TradingView's.

## Limits

- **Your Guard must be running and connected.** An alert that arrives while it is offline is not stored for later; a stale entry is worse than a missed one.
- **TradingView sees only that the relay answered**, not Guard's decision. Use Guard's own alerts (Telegram, planned) to see fills and refusals.
- **Latency.** TradingView to relay to Guard adds network time. Alerts suit decisions on closed bars, not speed.
