API WebSocket
Streaming de dados em tempo real para negociação de opções na Hypercall.
Confira a Referência interativa da API WebSocket para uma navegação melhor, com exemplos ao vivo e detalhes dos schemas.
Baixe a especificação AsyncAPI para uso programático.
Os objetos de mensagem do WebSocket podem ganhar novos campos sem aviso prévio. Os clientes devem ignorar campos que não reconhecem. A Hypercall não remove campos de um schema de mensagem existente.
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.
Compressão
/ws suporta permessage-deflate conforme a RFC 7692. Ative-o no seu cliente para reduzir a banda de dados de mercado: em tráfego de produção capturado, frames comprimidos de forma independente ficam cerca de 45% menores, e lotes de preços de índice cerca de 70% menores.
- Seu cliente oferece a extensão na requisição de upgrade, por exemplo
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits. - Quando o servidor aceita, a resposta
101trazSec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover. Cada mensagem do servidor é então comprimida isoladamente, sem uma janela deslizante compartilhada, e mensagens com menos de algumas centenas de bytes continuam sendo enviadas sem compressão. Sua biblioteca descomprime de forma transparente. - Se a resposta não tiver o header
Sec-WebSocket-Extensions, todos os frames vêm sem compressão. A compressão é ativada por ambiente, então os clientes precisam lidar com os dois casos; as mensagens JSON são idênticas em ambos. A produção ainda não aceita a extensão, então os frames de produção atualmente vêm sem compressão. - Seu cliente pode comprimir o que envia. O servidor descomprime cada mensagem do cliente até 256 KiB e fecha a conexão se uma mensagem ultrapassar esse limite.
Navegadores negociam a extensão automaticamente. A maioria das bibliotecas não precisa de nenhuma alteração:
Python (websockets ativa a compressão por padrão):
import websockets
async with websockets.connect("wss://api.hypercall.xyz/ws", compression="deflate") as ws:
...
Node.js (ws):
import WebSocket from "ws";
const ws = new WebSocket("wss://api.hypercall.xyz/ws", { perMessageDeflate: true });
Rust: o WsClient do hypercall-client negocia permessage-deflate por padrão (veja Exemplo: cliente Rust). tungstenite e tokio-tungstenite puros ainda não o implementam, então se conectam sem compressão. Para usar compressão com o seu próprio socket, use um cliente que a implemente, como o yawc:
let url = "wss://api.hypercall.xyz/ws".parse()?;
let mut ws = yawc::WebSocket::connect(url)
.with_options(yawc::Options::default().with_low_latency_compression())
.await?;
Identificação da carteira
Para receber dados em canais autenticados (ordens, execuções, portfólio), identifique sua carteira após conectar enviando uma mensagem Authenticate:
{"type": "Authenticate", "wallet": "0x1234..."}
O servidor responde com uma 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 query parameter ?wallet= ainda é suportado por compatibilidade, mas está obsoleto e será removido em uma versão futura. Prefira a abordagem baseada em mensagem descrita acima.
Liveness da conexão
O servidor aplica um heartbeat no WebSocket:
- Envia um frame de controle
Pinga cada 20 segundos - Espera um
Pongcorrespondente em até 60 segundos - Fecha a conexão com o código
1008e o motivopong timeoutse o cliente parar de responder
As implementações de WebSocket dos navegadores tratam ping/pong automaticamente. Muitas bibliotecas websocket em Rust, incluindo tungstenite e tokio-tungstenite, também tratam o ping/pong dos 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 com socket bruto devem responder aos frames Ping com Pong.
Recuperação de consumidor lento
O servidor fecha uma conexão /ws que não consegue escoar os 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 em 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 ultrapassou o limite de segurança. |
cause | message_limit, byte_limit, message_age ou write_timeout. |
recovery | Próxima ação obrigatória, como resubscribe, snapshot_resubscribe, portfolio_refetch ou rest_reconcile. |
Após qualquer desconexão, reconecte, identifique a carteira novamente quando necessário, refaça as inscrições e reconcilie o estado atual antes de processar novos eventos. Canais públicos ordenados exigem um snapshot novo. Canais de eventos privados exigem reconciliação pela superfície REST autoritativa, porque o replay por cursor ainda não está disponível. Uma conexão totalmente travada pode ser encerrada antes de conseguir ler o motivo do fechamento, então os clientes devem usar esse fluxo de recuperação também em fechamentos não limpos.
Use conexões separadas para dados de mercado públicos de alta frequência e para comandos autenticados ou streams privados. As classes de entrega selecionam métricas e o comportamento de recuperação, mas os frames de uma mesma 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 prazo 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"}
Filtro por símbolo
Os canais order_updates e fills suportam um filtro opcional symbols. Quando informado, o servidor só envia mensagens cujo ativo subjacente corresponda a um dos símbolos especificados.
{"type": "Subscribe", "channel": "order_updates", "symbols": ["BTC"]}
São aceitos tanto ativos subjacentes isolados ("BTC") quanto nomes completos de instrumentos ("BTC-20260131-100000-C"). 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.
Os canais públicos orderbook, trades, indicative_market_data e indicative_market_data_v2 também aceitam symbols, comparados com os símbolos exatos dos instrumentos (sem diferenciar maiúsculas e minúsculas), e não com os ativos subjacentes:
{"type": "Subscribe", "channel": "orderbook", "symbols": ["BTC-20260131-100000-C"]}
Sem symbols, esses canais entregam todos os instrumentos.
Filtro da grade de opções
O canal options_chain suporta filtro por símbolos de instrumentos, 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", comparada com a data de calendário UTC do vencimento de cada instrumento | Todos os vencimentos |
option_type | "call", "put", "both", ou omita para ambos | Ambos |
Entrega em lotes
indicative_market_data_v2 traz as mesmas linhas que indicative_market_data, com várias linhas por frame:
{"type": "Subscribe", "channel": "indicative_market_data_v2", "symbols": ["BTC-20260131-100000-C"]}
O servidor confirma com um marcador de lote:
{"type": "Subscribed", "channel": "indicative_market_data_v2", "batch": true}
Em seguida, ele retém cada linha por no máximo uma janela de lote, 25 ms por padrão, e envia as linhas publicadas nessa janela como um único frame IndicativeMarketDataBatch. Um lote também é enviado antes do prazo quando atinge 256 linhas.
- Detecção de suporte: somente uma confirmação com
"batch": trueindica que o servidor faz lotes. Servidores lançados antes deste canal confirmam qualquer nome de canal sem o marcador e depois não enviam nada, e os servidores atuais respondem a um canal desconhecido com um errounknown_channel. Em qualquer desses casos, cancele a inscrição emindicative_market_data_v2e inscreva-se emindicative_market_datana mesma conexão. - Latência: uma linha chega até uma janela mais tarde do que em
indicative_market_data. Em tráfego de produção com a janela padrão, as linhas esperaram 12 ms em média e nunca mais de 25 ms. Use o canal v1 se você reage a atualizações individuais de cotação em milissegundos. - Banda: os lotes sozinhos economizam cerca de 7% dos bytes. A grande economia vem com o
permessage-deflate: lotes comprimidos são cerca de 9,8 vezes menores que frames de uma linha sem compressão em uma inscrição sem filtro, e cerca de 8,3 vezes menores para uma única grade de opções, contra cerca de 1,8 vez para linhas comprimidas uma a uma. - Ordem: as linhas em
updatesestão em ordem de publicação, e os lotes chegam em ordem. As linhas não são consolidadas, então um mesmo instrumento pode aparecer mais de uma vez em um lote. Aplique as linhas em ordem e você chega ao mesmo estado que no canal v1. - Snapshot: o snapshot do momento da inscrição também chega como frames de lote.
- Filtros:
symbolsfunciona como emindicative_market_data. Restringir o filtro ou cancelar a inscrição primeiro envia as linhas já pendentes sob o filtro antigo e depois a confirmaçãoUnsubscribed, então nenhuma linha é perdida ou duplicada. - Canais independentes: uma conexão pode se inscrever nos dois canais com filtros diferentes.
indicative_market_datanão muda.
Canais disponíveis
| Canal | Requer autenticação | Descrição |
|---|---|---|
orderbook | Não | Atualizações L2 do livro de ofertas para todos os símbolos |
trades | Não | Feed público de negócios |
market_updates | Não | Mudanças na listagem de mercados (criados/removidos/vencidos) |
options_chain | Não | Atualizações incrementais da grade de opções (filtrável por symbols, expiry, option_type) |
index_prices | Não | Preços spot/de índice em tempo real para todos os ativos subjacentes |
indicative_market_data | Não | Stream de provedores de cotação em allowlist. Ainda não disponível de forma geral |
indicative_market_data_v2 | Não | O mesmo stream em frames de lote. Veja Entrega em lotes |
order_updates | Sim | Mudanças de status das suas ordens (filtrável por símbolo) |
fills | Sim | Execuções dos seus negócios (filtrável por símbolo) |
portfolio | Sim | Atualizações das suas posições e saldos |
liquidation | Sim | Mudanças no seu estado de liquidação |
rfq | Sim | Cotações de RFQ, atualizações de status e notificações de execução |
experimental_order_updates, experimental_combo_fills | Sim | Canais experimentais de combos. Veja API experimental de combos |
experimental_combo_trades | Não | Negócios públicos experimentais de combos |
Dados de competições e leaderboards não são servidos por api.hypercall.xyz, nem via WebSocket nem via REST. Inscrever-se nos canais descontinuados competition ou competition_engagement retorna um Error. O app de trading lê as competições via HTTP a partir do serviço de analytics em https://api-ui.hypercall.xyz, por exemplo GET /v2/competitions, GET /v2/competitions/{id}, GET /v2/competitions/leaderboard e GET /v2/competitions/all-time/leaderboard.
Tipos de mensagem
Enviar ordem (autenticado)
Envie uma ordem pelo caminho de comandos do 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": 1780000000000,
"signature": "0x..."
}
O nonce acima é ilustrativo. Gere um novo para cada ordem a partir do horário Unix atual em milissegundos, por exemplo Date.now() em JavaScript. Um valor fixo copiado fica fora da janela aceita e é rejeitado.
| Campo | Tipo | Descrição |
|---|---|---|
wallet | string | Endereço da carteira dona da ordem |
symbol | string | Símbolo da opção |
side | string | "Buy" ou "Sell" |
size | string | Tamanho do contrato, exatamente igual ao valor assinado |
price | string | Preço limite, exatamente igual ao valor assinado |
tif | string | Time-in-force opcional, padrão "gtc" |
route | string | Use "book_only". Uma rota omitida ainda é aceita, mas está obsoleta, e a data de descontinuação já passou. |
client_id | string | ID de ordem do cliente, opcional |
reduce_only | boolean | Opcional, padrão false. Uma ordem reduce-only assina PlaceOrderReduceOnly e exige uma route explícita |
mmp_enabled | boolean | Opcional, padrão false |
builder_code_address | string | Carteira de builder code, opcional |
nonce | integer | Nonce de assinatura obrigatório: o horário Unix atual em milissegundos. Deve estar entre dois dias antes e um dia depois do horário atual. Cada nonce é aceito uma única vez por signatário, e ordens REST e WebSocket compartilham o mesmo espaço de nonces. Um frame reenviado é rejeitado. |
signature | string | Assinatura EIP-712 PlaceOrder |
A conexão precisa ter enviado Authenticate antes; caso contrário, o servidor responde {"type": "Error", "message": "Not authenticated"}. O signatário deve ser a wallet ou um agente aprovado para ela.
O PlaceOrder via WebSocket atualmente envia a ordem diretamente ao livro de ofertas. route="best_execution" e route="rfq_only" são rejeitados no WebSocket porque esse caminho ainda não executa o roteamento RPI/RFQ. Use POST /order para best_execution.
Uma ordem admitida é respondida na mesma conexão com um OrderResult:
{
"type": "OrderResult",
"order_id": 12345,
"status": "ACKED",
"symbol": "BTC-20260131-100000-C",
"side": "Buy",
"price": "100",
"size": "1"
}
reason é incluído quando a ordem não é aceita. Falhas de validação, assinatura e rate limit são enviadas como um Error cujo message começa com Order placement failed:. Mudanças de status posteriores chegam em order_updates.
Atualização do livro de ofertas
Snapshot/atualização L2 do livro de ofertas para um símbolo.
{
"type": "OrderbookUpdate",
"symbol": "BTC-20260131-100000-C",
"option_token_address": "0x...",
"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 |
option_token_address | string? | Endereço on-chain do token da opção, quando conhecido |
bids | array | Níveis de bid como tuplas [price, size], com o tamanho em contratos legíveis |
asks | array | Níveis de ask como tuplas [price, size], com o tamanho em contratos legíveis |
timestamp | integer | Timestamp Unix (milissegundos) |
Negócio
Evento público de negócio.
{
"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 do negócio em USD |
size | string | Tamanho do negócio em contratos |
side | string | Lado agressor (Buy ou Sell) |
timestamp | integer | Timestamp Unix (milissegundos) |
Execução (autenticado)
Notificação de execução do seu negócio.
{
"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,
"builder_code_address": null,
"builder_code_fee": null,
"instrument_type": "option"
}
| 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 | Seu lado no negócio (Buy ou Sell) |
price | string | Preço de execução em USD |
size | string | Tamanho executado 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 da venue no lançamento estiverem desativadas |
trade_id | integer | ID único do negócio |
is_taker | boolean | Se você foi o taker |
builder_code_address | string? | Carteira de builder code (se houver) |
builder_code_fee | string? | Taxa de builder code. Retorna null enquanto as taxas da venue no lançamento estiverem desativadas |
instrument_type | string | "option" |
Atualização de portfólio (autenticado)
Enviada no canal portfolio para posições, saldos, margem e gregas. Na inscrição, o servidor envia um snapshot Initial e depois atualizações incrementais.
O type de cada frame é o tipo de atualização:
| Tipo de atualização | Campos além de timestamp |
|---|---|
Initial | positions |
PositionUpdate | position |
BalanceUpdate | total_margin_used |
MarginUpdate | span_margin, margin_summary, margin_mode, total_margin_used, available_balance |
GreeksUpdate | per_leg, aggregate |
O timestamp nos frames de portfolio é em segundos Unix, diferente dos timestamps em milissegundos dos outros canais.
Exemplo de atualização de gregas:
{
"type": "GreeksUpdate",
"timestamp": 1737331200,
"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 gregas usam:
per_leg: []aggregate: null
Atualização de ordem (autenticado)
Notificação de mudança de status da ordem. Atualizações PARTIALLY_FILLED são incluídas.
filled_size é a quantidade acumulada executada em todas as execuções da ordem,
não a quantidade da execução mais recente.
{
"type": "OrderUpdate",
"order_id": 12345,
"client_id": "my-order-1",
"request": {
"price": "0.0523",
"size": "10.0",
"symbol": "BTC-20260131-100000-C",
"side": "Buy",
"tif": "gtc"
},
"status": "PARTIALLY_FILLED",
"filled_size": "0.75",
"timestamp": 1767225600000,
"reason": null,
"wallet_address": "0x0000000000000000000000000000000000000001",
"instrument_type": "option"
}
status é um de ACKED, OPEN, PARTIALLY_FILLED, FILLED, CANCELED ou REJECTED. client_id é omitido quando a ordem não tem um. instrument_type é "option" ou "perp".
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
}
action é Created, Expired, Deleted (traz apenas symbol e timestamp) ou ComboCreated (veja API experimental de combos).
Atualização da grade de opções
Enviada em options_chain quando uma listagem ou o seu topo de livro muda. action é Upsert ou Remove.
{
"type": "OptionsChainUpdate",
"action": "Upsert",
"currency": "BTC",
"expiry": 1769846400,
"row": {
"strike": 100000.0,
"call": {
"symbol": "BTC-20260131-100000-C",
"bid_price_usd": 1520.0,
"ask_price_usd": 1580.0
}
},
"timestamp": 1737331200000
}
Uma linha Upsert traz uma perna (call ou put) com os campos de perna do snapshot da grade de opções: symbol, option_token_address, preço de bid e ask, IV, tamanho em contratos e nocional em USD, greeks_abs e greeks_cash (o exemplo acima está abreviado). Valores indisponíveis são null; a perna que não faz parte da atualização é omitida. Um Remove traz currency, expiry, strike, option_type, symbol e timestamp quando um instrumento é removido, vence ou entra em liquidação pendente. expiry é em segundos Unix.
Posição vencida (autenticado)
Enviada no canal portfolio quando a 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",
"settlement_entry_price": "100.0",
"cost_basis": "1000.0",
"net_pnl": "-500.0",
"timestamp": 1738281600000
}
settlement_entry_price, cost_basis e net_pnl são null quando não estão disponíveis.
Mudança de estado de liquidação (autenticado)
Enviada no canal liquidation quando o estado de liquidação da sua conta muda.
{
"type": "LiquidationStateChange",
"wallet_address": "0x1234...abcd",
"previous_state": "healthy",
"new_state": "pre_liquidation",
"liquidation_mode": "partial",
"margin_mode": "standard",
"equity": "10000.0",
"mm_required": "10500.0",
"maintenance_margin": "-500.0",
"shortfall": "500.0",
"partial_liquidation": { "...": "..." },
"full_liquidation": null,
"timestamp": 1737331200000
}
| Campo | Descrição |
|---|---|
previous_state, new_state | healthy, pre_liquidation, in_liquidation ou liquidated |
liquidation_mode | partial, full ou null quando nenhum fluxo de liquidação está ativo |
margin_mode | standard ou portfolio |
equity | Patrimônio da conta |
mm_required | Requisito de margem de manutenção |
maintenance_margin | Patrimônio menos o requisito de margem de manutenção |
shortfall | Quanto a conta está abaixo da margem de manutenção |
partial_liquidation | Andamento da liquidação parcial (horário de entrada, patrimônio alvo, prazo de escalonamento, ordens de liquidação ativas, bônus) ou null |
full_liquidation | Detalhes do leilão de liquidação total (ID do leilão, horário de início, margem necessária e, ao final, o vencedor, o bônus e a transação de resolução) ou null |
Veja Liquidação para o significado de cada estado.
Atualização de preço de índice
Preços spot/de índice em lote para todos os ativos subjacentes.
{
"type": "IndexPriceUpdate",
"prices": [
{"underlying": "BTC", "price": "97250.50", "timestamp": 1737331200000},
{"underlying": "ETH", "price": "3200.00", "timestamp": 1737331199980},
{"underlying": "NVDA", "price": "182.40", "timestamp": 1737331199950}
],
"timestamp": 1737331199950
}
| Campo | Tipo | Descrição |
|---|---|---|
prices | array | Array de entradas {underlying, price, timestamp} para cada ativo subjacente acompanhado |
prices[].underlying | string | Símbolo do ativo subjacente (ex.: "BTC", "ETH") |
prices[].price | string | Preço spot/de índice atual em USD |
prices[].timestamp | integer | Horário de observação na fonte para este ativo subjacente, em milissegundos Unix |
timestamp | integer | Horário de observação na fonte mais antigo incluído em prices, em milissegundos Unix |
Dados de mercado indicativos
Stream de provedores de cotação em allowlist, com o melhor bid/ask agregado dos provedores de cotação registrados. Este canal ainda não está disponível de forma geral. Use os dados de mercado via REST e os canais autenticados de ordens/execuções/portfólio, a menos que a Hypercall tenha habilitado o streaming de provedores de cotação 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,
"published_at": 1737331200004,
"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ção ativos |
rfq_provider_quotes | array | Cotações por provedor, opcional: wallet, bid_price, ask_price, max_bid_size, max_ask_size, updated_at |
published_at | integer | Horário do relógio da API em que esta linha foi publicada, em milissegundos Unix. Meça a idade de rfq_provider_quotes em relação a ele, não ao seu relógio local |
timestamp | integer | Horário da cotação mais recente de um provedor, em milissegundos Unix |
Lote de dados de mercado indicativos
Enviado no canal indicative_market_data_v2 (veja Entrega em lotes). Cada elemento de updates tem os mesmos campos de uma mensagem IndicativeMarketData, sem type.
{
"type": "IndicativeMarketDataBatch",
"flushed_at": 1737331200025,
"updates": [
{
"instrument": "BTC-20260131-100000-C",
"best_bid": "0.0520",
"best_ask": "0.0530",
"num_providers": 3,
"published_at": 1737331200004,
"timestamp": 1737331200000
},
{
"instrument": "BTC-20260131-105000-C",
"best_bid": "0.0310",
"best_ask": "0.0322",
"num_providers": 2,
"published_at": 1737331200004,
"timestamp": 1737331200001
}
]
}
| Campo | Tipo | Descrição |
|---|---|---|
flushed_at | integer | Horário do relógio da API em que o lote foi enviado, em milissegundos Unix. flushed_at - published_at é o atraso de lote de uma linha |
updates | array | Linhas publicadas durante a janela, da mais antiga para a mais recente |
Cotações de RFQ (autenticado)
Cotações recebidas em resposta ao seu envio 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 de um RFQ que você enviou.
{
"type": "RfqStatusUpdate",
"rfq_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "executed",
"taker_wallet": "0x1234...abcd"
}
Sincronização de relógio
Envie ClockSync com qualquer string de nonce para obter uma amostra do relógio da API na mesma conexão, por exemplo para limitar o atraso de transporte:
{"type": "ClockSync", "nonce": "abc"}
{"type": "ClockSynced", "nonce": "abc", "server_at": 1737331200000}
server_at é em milissegundos Unix. Não é necessária autenticação.
Erro
Mensagem de erro do servidor. Faça a correspondência por code quando presente, não por message.
{
"type": "Error",
"message": "Unknown channel 'foobar'",
"code": "unknown_channel",
"channel": "foobar"
}
| Campo | Tipo | Descrição |
|---|---|---|
message | string | Descrição legível |
code | string | Motivo estável, opcional. unknown_channel: um Subscribe indicou um canal que não existe |
channel | string | Opcional. O canal indicado por um Subscribe rejeitado |
Um Subscribe para um canal que não está listado em Canais disponíveis é respondido com o erro unknown_channel, e nenhuma inscrição é criada. Servidores anteriores confirmavam qualquer nome e depois não enviavam nada.
Outros erros trazem apenas message. Exemplos: Authentication required for this channel quando você se inscreve em um canal autenticado antes de Authenticate, Not authenticated para PlaceOrder antes de Authenticate, e Invalid wallet address: ....
Autenticação
Canais autenticados exigem uma mensagem de identificação da carteira após a conexão:
{"type": "Authenticate", "wallet": "0x1234567890abcdef..."}
As mensagens em canais autenticados são filtradas para mostrar apenas os dados da sua carteira. Nenhuma assinatura é exigida 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`);
}
};
Exemplo: cliente Rust
O WsClient do crate hypercall-client identifica a carteira, restaura as inscrições após uma reconexão e responde aos pings do servidor para você. Ele oferece compressão permessage-deflate por padrão e volta para frames sem compressão quando o servidor não a aceita, com as mesmas mensagens JSON em ambos os casos. Chame with_permessage_deflate(false) para deixar de oferecê-la.
use hypercall_client::WsClient;
#[tokio::main]
async fn main() -> hypercall_client::Result<()> {
let ws = WsClient::new();
ws.connect("https://api.hypercall.xyz", None).await?;
ws.subscribe(vec!["orderbook", "index_prices"]).await?;
if let Some(msg) = ws.wait_for_message(|_| true, 5_000).await {
println!("Received: {msg:?}");
}
Ok(())
}