WebSocket API
Streaming de dados em tempo real para negociação de opções na Hypercall.
Confira a Referência Interativa da WebSocket API para uma experiência de navegação melhor, com exemplos ao vivo e detalhes de schema.
Baixe a especificação AsyncAPI para uso programático.
Conexão
Conecte-se a wss://HOST/ws:
Endpoints:
- Produção:
wss://api.hypercall.xyz/ws - Local:
ws://localhost:3000/ws
A testnet está temporariamente desativada até que a Hypercall adquira mais HYPE de testnet.
Identificação da Carteira
Para receber dados em canais autenticados (ordens, fills, portfólio), identifique sua carteira após conectar enviando uma mensagem Authenticate:
{"type": "Authenticate", "wallet": "0x1234..."}
O servidor responde com a confirmação:
{"type": "Authenticated", "wallet": "0x1234..."}
Após receber Authenticated, você pode se inscrever em canais autenticados. Se o endereço da carteira for inválido, o servidor responde com uma mensagem Error e a conexão permanece aberta.
O parâmetro de query ?wallet= ainda é suportado para compatibilidade retroativa, mas está descontinuado e será removido em uma versão futura. Prefira a abordagem baseada em mensagens acima.
Vitalidade da Conexão
O servidor impõe um heartbeat de WebSocket:
- Envia um frame de controle
Pinga cada 20 segundos - Espera um
Pongcorrespondente dentro de 60 segundos - Fecha a conexão com o código de fechamento
1008e o motivopong timeoutse o cliente parar de responder
As implementações de WebSocket de navegadores lidam com ping/pong automaticamente. Muitas bibliotecas de websocket em Rust, incluindo tungstenite e tokio-tungstenite, também lidam com o ping/pong de frames de controle para você. Consulte a documentação da biblioteca do seu cliente antes de adicionar tratamento manual de Pong. Implementações customizadas ou de socket bruto devem responder a frames Ping com Pong.
Recuperação de Consumidor Lento
O servidor fecha uma conexão /ws que não consegue drenar dados de saída dentro do teto de segurança configurado de mensagens, bytes codificados, idade da fila ou escrita no socket. Quando a conexão ainda consegue aceitar um frame de fechamento, o servidor usa o código 1008 e um motivo JSON compacto:
{"error":"slow_consumer","class":"ordered_public","cause":"message_age","recovery":"snapshot_resubscribe"}
Os campos do motivo são:
| Campo | Significado |
|---|---|
class | Classe de entrega cujo frame cruzou o limite de segurança. |
cause | message_limit, byte_limit, message_age ou write_timeout. |
recovery | Próxima ação necessária, como resubscribe, snapshot_resubscribe, portfolio_refetch ou rest_reconcile. |
Após qualquer desconexão, reconecte, identifique a carteira novamente quando necessário, reinscreva-se e reconcilie o estado atual antes de processar novos eventos. Canais públicos ordenados exigem um novo snapshot. Canais de eventos privados exigem reconciliação através da superfície REST autoritativa, porque o replay de cursor ainda não está disponível. Uma conexão totalmente travada pode terminar antes de conseguir ler o motivo do fechamento, então os clientes devem usar esse fluxo de recuperação também para um fechamento não limpo.
Use conexões separadas para dados de mercado públicos de alta taxa e comandos autenticados ou streams privados. As classes de entrega selecionam métricas e comportamento de recuperação, mas os frames em uma conexão ainda compartilham um único caminho ordenado de escrita no socket. Uma escrita pública travada pode, portanto, atrasar frames privados posteriores nessa mesma conexão até que o deadline de escrita a feche.
Inscrição em Canais
Envie uma mensagem JSON para se inscrever:
{"type": "Subscribe", "channel": "orderbook"}
Para cancelar a inscrição:
{"type": "Unsubscribe", "channel": "orderbook"}
Você receberá uma confirmação:
{"type": "Subscribed", "channel": "orderbook"}
Filtragem por Símbolo
Os canais order_updates e fills suportam um filtro symbols opcional. Quando fornecido, o servidor só envia mensagens cujo ativo subjacente corresponde a um dos símbolos especificados.
{"type": "Subscribe", "channel": "order_updates", "symbols": ["BTC"]}
Tanto ativos subjacentes simples ("BTC") quanto nomes completos de instrumentos ("BTC-20260131-100000-C") são aceitos. Para adicionar mais símbolos, envie outro Subscribe. Para remover símbolos específicos:
{"type": "Unsubscribe", "channel": "order_updates", "symbols": ["BTC"]}
Quando nenhum symbols é especificado, todas as atualizações da sua carteira são encaminhadas.
Filtragem da Cadeia de Opções
O canal options_chain suporta filtragem por símbolos de ativo subjacente, data de vencimento e tipo de opção:
{
"type": "Subscribe",
"channel": "options_chain",
"symbols": ["BTC-20260131-100000-C"],
"expiry": "2026-01-31",
"option_type": "call"
}
| Filtro | Valores | Padrão |
|---|---|---|
symbols | Array de símbolos completos de instrumentos (ex.: ["BTC-20260131-100000-C"]) | Todos os instrumentos |
expiry | String de data "YYYY-MM-DD" | Todos os vencimentos |
option_type | "call", "put" ou omita para ambos | Ambos |
Canais Disponíveis
| Canal | Autenticação Necessária | Descrição |
|---|---|---|
orderbook | Não | Atualizações de livro de ofertas L2 para todos os símbolos |
trades | Não | Feed público de negociações |
market_updates | Não | Mudanças de listagem de mercado (criado/excluído/vencido) |
options_chain | Não | Atualizações incrementais da cadeia de opções (filtrável por symbols, expiry, option_type) |
index_prices | Não | Preços spot/índice em tempo real para todos os ativos subjacentes |
indicative_market_data | Não | Stream de provedor de cotações em allowlist. Ainda não disponível de forma geral |
order_updates | Sim | Mudanças de status das suas ordens (filtrável por símbolo) |
fills | Sim | Seus fills de negociação (filtrável por símbolo) |
portfolio | Sim | Atualizações da sua posição e saldo |
liquidation | Sim | Mudanças no seu estado de liquidação |
competition | Sim | Seu resumo de PnL de competição, rank e estatísticas finais |
competition_engagement | Sim | Mudanças de rank, gap para o próximo rank e classificação final |
rfq | Sim | Cotações de RFQ, atualizações de status e notificações de fill |
Tipos de Mensagem
Enviar Ordem (Autenticado)
Envie uma ordem através do caminho de comando da WebSocket.
{
"type": "PlaceOrder",
"wallet": "0x1234...",
"symbol": "BTC-20260131-100000-C",
"side": "Buy",
"size": "1",
"price": "100",
"tif": "gtc",
"route": "book_only",
"client_id": "my-order-1",
"nonce": 1000,
"signature": "0x..."
}
| Campo | Tipo | Descrição |
|---|---|---|
wallet | string | Endereço da carteira que é dona da ordem |
symbol | string | Símbolo da opção |
side | string | "Buy" ou "Sell" |
size | string | Tamanho do contrato, correspondendo exatamente ao valor assinado |
price | string | Preço limite, correspondendo exatamente ao valor assinado |
tif | string | Time-in-force opcional, padrão "gtc" |
route | string | Rota opcional. Use "book_only" para ordens WebSocket cientes de rota. A omissão da rota permanece aceita até pelo menos 4 de julho de 2026. |
client_id | string | ID de ordem do cliente opcional |
nonce | integer | Nonce único de assinatura |
signature | string | Assinatura EIP-712 PlaceOrder |
O PlaceOrder da WebSocket atualmente despacha diretamente para o livro de ofertas. route="best_execution" e route="rfq_only" são rejeitados na WebSocket porque esse caminho ainda não executa roteamento RPI/RFQ. Use POST /order para best_execution.
Atualização do Livro de Ofertas
Snapshot/atualização de livro de ofertas L2 para um símbolo.
{
"type": "OrderbookUpdate",
"symbol": "BTC-20260131-100000-C",
"bids": [["95000.5", "10.5"], ["94999.0", "25.0"]],
"asks": [["95001.0", "8.0"], ["95002.5", "15.0"]],
"timestamp": 1737331200000
}
| Campo | Tipo | Descrição |
|---|---|---|
symbol | string | Símbolo da opção |
bids | array | Níveis de bid como tuplas [price, size], o tamanho é em contratos legíveis por humanos |
asks | array | Níveis de ask como tuplas [price, size], o tamanho é em contratos legíveis por humanos |
timestamp | integer | Timestamp Unix (milissegundos) |
Negociação
Evento público de negociação.
{
"type": "Trade",
"symbol": "BTC-20260131-100000-C",
"price": "0.0523",
"size": "5.0",
"side": "buy",
"timestamp": 1737331200000
}
| Campo | Tipo | Descrição |
|---|---|---|
symbol | string | Símbolo da opção |
price | string | Preço da negociação em USD |
size | string | Tamanho da negociação em contratos |
side | string | Lado agressor (buy ou sell) |
timestamp | integer | Timestamp Unix (milissegundos) |
Fill (Autenticado)
Notificação do seu fill de negociação.
{
"type": "Fill",
"order_id": 12345,
"fill_id": 67890,
"symbol": "BTC-20260131-100000-C",
"side": "buy",
"price": "0.0523",
"size": "5.0",
"timestamp": 1737331200000,
"wallet_address": "0x1234...abcd",
"fee": "0",
"trade_id": 99999,
"is_taker": true
}
| Campo | Tipo | Descrição |
|---|---|---|
order_id | integer | ID da sua ordem |
fill_id | integer | ID da execução |
symbol | string | Símbolo da opção |
side | string | Lado da negociação (buy ou sell) |
price | string | Preço da execução em USD |
size | string | Tamanho da execução em contratos |
timestamp | integer | Timestamp Unix (milissegundos) |
wallet_address | string | Endereço da sua carteira |
fee | string | Taxa de negociação cobrada. Retorna 0 enquanto as taxas do venue de lançamento estiverem desativadas |
trade_id | integer | ID único da negociação |
is_taker | boolean | Se você foi o taker |
builder_code_address | string? | Carteira do builder code (se houver) |
builder_code_fee | string? | Taxa do builder code. Retorna null enquanto as taxas do venue de lançamento estiverem desativadas |
Atualização de Portfólio (Autenticado)
Atualização do stream de portfólio para posições, saldos, margem e Greeks.
Exemplo de atualização de Greeks:
{
"type": "PortfolioUpdate",
"timestamp": 1737331200000,
"per_leg": [
{
"symbol": "BTC-20260131-100000-C",
"quantity": "2.0",
"delta": 0.91,
"gamma": 0.003,
"theta": -0.12,
"vega": 0.44,
"iv": 0.63
}
],
"aggregate": {
"delta": 0.91,
"gamma": 0.003,
"theta": -0.12,
"vega": 0.44,
"iv": 0.63
}
}
Para portfólios vazios, as atualizações de Greeks usam:
per_leg: []aggregate: null
Resumo de PnL da Competição (Autenticado)
Atualização do stream de competição para exibição de PnL no cabeçalho/rodapé.
{
"type": "CompetitionPnlSummary",
"wallet_address": "0x1234...abcd",
"lifetime_realized_pnl": "1250.50",
"active_competition": {
"competition_id": 7,
"competition_name": "Spring Sprint",
"competition_state": "active",
"rank": 12,
"pnl": "420.25",
"volume": "25000",
"efficiency": "0.01681",
"medal": null
},
"timestamp": 1737331200000
}
Quando não há competição ativa, active_competition é null.
Atualização de Ordem (Autenticado)
Notificação de mudança de status da ordem.
{
"type": "OrderUpdate",
"order_id": 12345,
"client_order_id": "my-order-1",
"status": "filled",
"filled_size": "10.0",
"remaining_size": "0",
"avg_fill_price": "0.0523"
}
Atualização de Mercado
Mudanças na listagem de mercados.
Mercado Criado:
{
"type": "MarketUpdate",
"action": "Created",
"symbol": "BTC-20260131-100000-C",
"strike": "100000",
"is_call": true,
"underlying": "BTC",
"expiry": 1738281600,
"timestamp": 1737331200000
}
Mercado Vencido:
{
"type": "MarketUpdate",
"action": "Expired",
"symbol": "BTC-20260131-100000-C",
"strike": "100000",
"is_call": true,
"underlying": "BTC",
"expiry": 1738281600,
"timestamp": 1738281600000
}
Posição Vencida (Autenticado)
Notificação quando sua posição é liquidada no vencimento.
{
"type": "PositionExpired",
"wallet_address": "0x1234...abcd",
"symbol": "BTC-20260131-100000-C",
"position_size": "10.0",
"settlement_price": "105000",
"settlement_value": "500.0",
"timestamp": 1738281600000
}
Mudança de Estado de Liquidação (Autenticado)
Mudança no estado de liquidação da sua conta.
{
"type": "LiquidationStateChange",
"wallet_address": "0x1234...abcd",
"previous_state": "Normal",
"new_state": "Warning",
"equity": "10000.0",
"mm_required": "9500.0",
"shortfall": "0",
"auction_id": null,
"timestamp": 1737331200000
}
| Estado | Descrição |
|---|---|
Normal | A conta está saudável |
Warning | Aproximando-se da chamada de margem |
Liquidating | Leilão de liquidação ativo |
Atualização de Preço de Índice
Preços spot/índice agrupados para todos os ativos subjacentes.
{
"type": "IndexPriceUpdate",
"prices": [
{"underlying": "BTC", "price": "97250.50"},
{"underlying": "ETH", "price": "3200.00"},
{"underlying": "HYPE", "price": "28.50"}
],
"timestamp": 1737331200000
}
| Campo | Tipo | Descrição |
|---|---|---|
prices | array | Array de entradas {underlying, price} para cada ativo subjacente rastreado |
prices[].underlying | string | Símbolo do ativo subjacente (ex.: "BTC", "ETH") |
prices[].price | string | Preço spot/índice atual em USD |
timestamp | integer | Timestamp Unix (milissegundos) |
Dados de Mercado Indicativos
Stream de provedores de cotações na allowlist com melhor bid/ask agregado dos provedores de cotações registrados. Este canal ainda não está disponível de forma geral. Use os dados de mercado via REST e os canais autenticados de ordem/execução/portfólio, a menos que a Hypercall tenha habilitado o streaming de provedores de cotações para a sua integração.
{
"type": "IndicativeMarketData",
"instrument": "BTC-20260131-100000-C",
"best_bid": "0.0520",
"best_ask": "0.0530",
"indicative_bid_size": "50.0",
"indicative_ask_size": "25.0",
"num_providers": 3,
"timestamp": 1737331200000
}
| Campo | Tipo | Descrição |
|---|---|---|
instrument | string | Símbolo da opção |
best_bid | string | Melhor preço de bid agregado (opcional) |
best_ask | string | Melhor preço de ask agregado (opcional) |
bid_iv | number | Volatilidade implícita do melhor bid (opcional) |
ask_iv | number | Volatilidade implícita do melhor ask (opcional) |
indicative_bid_size | string | Tamanho total de bid entre os provedores (opcional) |
indicative_ask_size | string | Tamanho total de ask entre os provedores (opcional) |
num_providers | integer | Número de provedores de cotações ativos |
timestamp | integer | Timestamp Unix (milissegundos) |
Mudança de Rank na Competição (Autenticado)
Notificação quando seu rank muda em uma competição ativa.
{
"type": "CompetitionRankChange",
"wallet_address": "0x1234...abcd",
"competition_id": 7,
"from_rank": 15,
"to_rank": 12,
"delta_places": 3,
"pnl": "420.25",
"timestamp": 1737331200000
}
Atualização de Gap da Competição (Autenticado)
Distância até o rank imediatamente acima do seu.
{
"type": "CompetitionGapUpdate",
"wallet_address": "0x1234...abcd",
"competition_id": 7,
"rank": 12,
"next_rank": 11,
"gap_metric_value": "50.00",
"timestamp": 1737331200000
}
Classificação Final da Competição (Autenticado)
Enviada quando uma competição termina com seus resultados finais.
{
"type": "CompetitionFinalStanding",
"wallet_address": "0x1234...abcd",
"competition_id": 7,
"rank": 12,
"pnl": "420.25",
"volume": "25000",
"efficiency": "0.01681",
"medal": null,
"timestamp": 1737331200000
}
Cotações de RFQ (Autenticado)
Cotações recebidas em resposta à sua submissão de RFQ.
{
"type": "RfqQuotes",
"rfq_id": "550e8400-e29b-41d4-a716-446655440000",
"quotes": [
{
"quote_id": "660e8400-e29b-41d4-a716-446655440001",
"net_premium": "52.30",
"expires_at": 1737331225000
}
],
"status": "quoted",
"taker_wallet": "0x1234...abcd"
}
Atualização de Status de RFQ (Autenticado)
Mudança de status para um RFQ que você submeteu.
{
"type": "RfqStatusUpdate",
"rfq_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "executed",
"taker_wallet": "0x1234...abcd"
}
Erro
Mensagem de erro do servidor.
{
"type": "Error",
"message": "Invalid channel: foobar"
}
Autenticação
Os canais autenticados exigem uma mensagem de identificação de carteira após a conexão:
{"type": "Authenticate", "wallet": "0x1234567890abcdef..."}
As mensagens nos canais autenticados são filtradas para mostrar apenas os dados da sua carteira. Nenhuma assinatura é necessária para conexões WebSocket.
Exemplo: Cliente Python
import asyncio
import websockets
import json
async def main():
uri = "wss://api.hypercall.xyz/ws"
async with websockets.connect(uri) as ws:
# Identify the wallet before subscribing to authenticated channels.
await ws.send(json.dumps({
"type": "Authenticate",
"wallet": "0xYourWallet"
}))
# Subscribe to orderbook
await ws.send(json.dumps({
"type": "Subscribe",
"channel": "orderbook"
}))
# Subscribe to fills for BTC only
await ws.send(json.dumps({
"type": "Subscribe",
"channel": "fills",
"symbols": ["BTC"]
}))
# Listen for messages
async for message in ws:
data = json.loads(message)
print(f"Received: {data['type']}")
asyncio.run(main())
Exemplo: Cliente TypeScript
const ws = new WebSocket("wss://api.hypercall.xyz/ws");
ws.onopen = () => {
ws.send(JSON.stringify({ type: "Authenticate", wallet: "0xYourWallet" }));
// Subscribe to channels
ws.send(JSON.stringify({ type: "Subscribe", channel: "orderbook" }));
// Subscribe to order updates filtered to BTC
ws.send(JSON.stringify({
type: "Subscribe",
channel: "order_updates",
symbols: ["BTC"],
}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
console.log(`Received: ${msg.type}`);
if (msg.type === "OrderbookUpdate") {
console.log(`${msg.symbol}: ${msg.bids.length} bids, ${msg.asks.length} asks`);
}
};