API WebSocket
Transmisión de datos en tiempo real para el trading de opciones en Hypercall.
Consulte la Referencia interactiva de la API WebSocket para una mejor experiencia de navegación, con ejemplos en vivo y detalles de los esquemas.
Descargue la especificación AsyncAPI para uso programático.
Los objetos de mensaje de WebSocket pueden incorporar nuevos campos sin previo aviso. Los clientes deben ignorar los campos que no reconozcan. Hypercall no elimina campos de un esquema de mensaje existente.
Conexión
Conéctese a wss://HOST/ws:
Endpoints:
- Producción:
wss://api.hypercall.xyz/ws - Local:
ws://localhost:3000/ws
La testnet está deshabilitada temporalmente hasta que Hypercall obtenga más HYPE de testnet.
Compresión
/ws admite permessage-deflate de RFC 7692. Actívelo en su cliente para reducir el ancho de banda de datos de mercado: en tráfico de producción capturado, los frames comprimidos de forma independiente son aproximadamente un 45% más pequeños, y los lotes de precios índice aproximadamente un 70% más pequeños.
- Su cliente ofrece la extensión en la solicitud de upgrade, por ejemplo
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits. - Cuando el servidor la acepta, la respuesta
101incluyeSec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover. A partir de ahí, cada mensaje del servidor se comprime por separado, sin una ventana deslizante compartida, y los mensajes de menos de unos cientos de bytes se siguen enviando sin comprimir. Su librería los descomprime de forma transparente. - Si la respuesta no tiene el encabezado
Sec-WebSocket-Extensions, todos los frames van sin comprimir. La compresión se habilita por entorno, por lo que los clientes deben manejar ambos casos; los mensajes JSON son idénticos en cualquiera de ellos. Producción todavía no acepta la extensión, así que los frames de producción actualmente van sin comprimir. - Su cliente puede comprimir lo que envía. El servidor descomprime cada mensaje del cliente hasta 256 KiB y cierra la conexión si un mensaje superara ese límite.
Los navegadores negocian la extensión automáticamente. La mayoría de las librerías no requieren cambios:
Python (websockets habilita la compresión por defecto):
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: el WsClient de hypercall-client negocia permessage-deflate por defecto (consulte Ejemplo: cliente en Rust). tungstenite y tokio-tungstenite sin extensiones todavía no lo implementan, por lo que se conectan sin compresión. Para usar compresión con su propio socket, utilice un cliente que la implemente, como 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?;
Identificación de la wallet
Para recibir datos en los canales autenticados (órdenes, ejecuciones, portafolio), identifique su wallet después de conectarse enviando un mensaje Authenticate:
{"type": "Authenticate", "wallet": "0x1234..."}
El servidor responde con una confirmación:
{"type": "Authenticated", "wallet": "0x1234..."}
Después de recibir Authenticated, puede suscribirse a los canales autenticados. Si la dirección de la wallet no es válida, el servidor responde con un mensaje Error y la conexión permanece abierta.
El parámetro de consulta ?wallet= sigue siendo compatible por razones de retrocompatibilidad, pero está obsoleto y se eliminará en una versión futura. Prefiera el método basado en mensajes descrito arriba.
Actividad de la conexión
El servidor aplica un heartbeat de WebSocket:
- Envía un frame de control
Pingcada 20 segundos - Espera el
Pongcorrespondiente en un plazo de 60 segundos - Cierra la conexión con el código de cierre
1008y el motivopong timeoutsi el cliente deja de responder
Las implementaciones de WebSocket en los navegadores gestionan ping/pong automáticamente. Muchas librerías de websocket en Rust, incluidas tungstenite y tokio-tungstenite, también gestionan por usted el ping/pong de los frames de control. Revise la documentación de la librería de su cliente antes de añadir un manejo manual de Pong. Las implementaciones personalizadas o de socket sin procesar deben responder a los frames Ping con Pong.
Recuperación de consumidores lentos
El servidor cierra una conexión /ws que no logra vaciar los datos salientes dentro del límite de seguridad configurado de mensajes, bytes codificados, antigüedad en cola o escritura en el socket. Cuando la conexión todavía puede aceptar un frame de cierre, el servidor usa el código 1008 y un motivo JSON compacto:
{"error":"slow_consumer","class":"ordered_public","cause":"message_age","recovery":"snapshot_resubscribe"}
Los campos del motivo son:
| Campo | Significado |
|---|---|
class | Clase de entrega cuyo frame superó el límite de seguridad. |
cause | message_limit, byte_limit, message_age o write_timeout. |
recovery | Acción siguiente requerida, como resubscribe, snapshot_resubscribe, portfolio_refetch o rest_reconcile. |
Tras cualquier desconexión, vuelva a conectarse, identifique de nuevo la wallet cuando sea necesario, vuelva a suscribirse y concilie el estado actual antes de procesar nuevos eventos. Los canales públicos ordenados requieren un snapshot nuevo. Los canales privados de eventos requieren conciliación a través de la interfaz REST autoritativa, porque la reproducción por cursor todavía no está disponible. Una conexión completamente bloqueada puede terminar antes de poder leer el motivo del cierre, por lo que los clientes deben aplicar este flujo de recuperación también ante un cierre no limpio.
Use conexiones separadas para los datos de mercado públicos de alta frecuencia y para los comandos autenticados o los streams privados. Las clases de entrega determinan las métricas y el comportamiento de recuperación, pero los frames de una misma conexión comparten una única ruta de escritura ordenada en el socket. Por lo tanto, una escritura pública bloqueada puede retrasar frames privados posteriores en esa misma conexión hasta que el plazo de escritura la cierre.
Suscripción a canales
Envíe un mensaje JSON para suscribirse:
{"type": "Subscribe", "channel": "orderbook"}
Para cancelar la suscripción:
{"type": "Unsubscribe", "channel": "orderbook"}
Recibirá una confirmación:
{"type": "Subscribed", "channel": "orderbook"}
Filtrado por símbolo
Los canales order_updates y fills admiten un filtro opcional symbols. Cuando se proporciona, el servidor solo envía los mensajes cuyo activo subyacente coincide con uno de los símbolos especificados.
{"type": "Subscribe", "channel": "order_updates", "symbols": ["BTC"]}
Se aceptan tanto activos subyacentes sin más ("BTC") como nombres completos de instrumentos ("BTC-20260131-100000-C"). Para añadir más símbolos, envíe otro Subscribe. Para eliminar símbolos concretos:
{"type": "Unsubscribe", "channel": "order_updates", "symbols": ["BTC"]}
Cuando no se especifica symbols, se reenvían todas las actualizaciones de su wallet.
Los canales públicos orderbook, trades, indicative_market_data e indicative_market_data_v2 también aceptan symbols, que se comparan con los símbolos exactos de los instrumentos (sin distinguir mayúsculas y minúsculas), no con los activos subyacentes:
{"type": "Subscribe", "channel": "orderbook", "symbols": ["BTC-20260131-100000-C"]}
Sin symbols, estos canales entregan todos los instrumentos.
Filtrado de la cadena de opciones
El canal options_chain admite filtrado por símbolos de instrumento, fecha de vencimiento y tipo de opción:
{
"type": "Subscribe",
"channel": "options_chain",
"symbols": ["BTC-20260131-100000-C"],
"expiry": "2026-01-31",
"option_type": "call"
}
| Filtro | Valores | Por defecto |
|---|---|---|
symbols | Array de símbolos completos de instrumentos (p. ej., ["BTC-20260131-100000-C"]) | Todos los instrumentos |
expiry | Cadena de fecha "YYYY-MM-DD", comparada con la fecha de calendario UTC del vencimiento de cada instrumento | Todos los vencimientos |
option_type | "call", "put", "both", u omitido para ambos | Ambos |
Entrega por lotes
indicative_market_data_v2 transporta las mismas filas que indicative_market_data, con varias filas por frame:
{"type": "Subscribe", "channel": "indicative_market_data_v2", "symbols": ["BTC-20260131-100000-C"]}
El servidor confirma con un marcador de lote:
{"type": "Subscribed", "channel": "indicative_market_data_v2", "batch": true}
A continuación retiene cada fila como máximo durante una ventana de lote, 25 ms por defecto, y envía las filas publicadas en esa ventana como un único frame IndicativeMarketDataBatch. Un lote también se envía antes de tiempo en cuanto alcanza 256 filas.
- Detección de compatibilidad: solo una confirmación con
"batch": trueindica que el servidor agrupa por lotes. Los servidores publicados antes de este canal confirman cualquier nombre de canal sin el marcador y luego no envían nada, y los servidores actuales responden a un canal desconocido con un errorunknown_channel. En cualquiera de los dos casos, cancele la suscripción aindicative_market_data_v2y suscríbase aindicative_market_dataen la misma conexión. - Latencia: una fila llega hasta una ventana más tarde que en
indicative_market_data. En tráfico de producción con la ventana por defecto, las filas esperaron 12 ms de media y nunca más de 25 ms. Use el canal v1 si actúa sobre actualizaciones individuales de cotizaciones en cuestión de milisegundos. - Ancho de banda: el agrupamiento por sí solo ahorra alrededor de un 7% de bytes. El gran ahorro llega con
permessage-deflate: los lotes comprimidos son unas 9,8 veces más pequeños que los frames de una fila sin comprimir para una suscripción sin filtro, y unas 8,3 veces más pequeños para una sola cadena de opciones, frente a unas 1,8 veces para filas comprimidas una a una. - Orden: las filas de
updatesestán en orden de publicación, y los lotes llegan en orden. Las filas no se consolidan, por lo que un mismo instrumento puede aparecer más de una vez en un lote. Aplique las filas en orden y llegará al mismo estado que en el canal v1. - Snapshot: el snapshot del momento de la suscripción también llega como frames de lote.
- Filtros:
symbolsfunciona igual que enindicative_market_data. Al restringir el filtro o cancelar la suscripción, primero se envían las filas que ya estaban pendientes con el filtro anterior y después la confirmaciónUnsubscribed, de modo que ninguna fila se pierde ni se duplica. - Canales independientes: una conexión puede suscribirse a ambos canales con filtros distintos.
indicative_market_datano cambia.
Canales disponibles
| Canal | Requiere autenticación | Descripción |
|---|---|---|
orderbook | No | Actualizaciones L2 del libro de órdenes para todos los símbolos |
trades | No | Feed público de operaciones |
market_updates | No | Cambios en los listados de mercados (creados/eliminados/vencidos) |
options_chain | No | Actualizaciones incrementales de la cadena de opciones (filtrable por symbols, expiry, option_type) |
index_prices | No | Precios spot/índice en tiempo real para todos los activos subyacentes |
indicative_market_data | No | Stream de proveedores de cotizaciones en lista de permitidos. Aún no está disponible de forma general |
indicative_market_data_v2 | No | El mismo stream en frames de lote. Consulte Entrega por lotes |
order_updates | Sí | Cambios de estado de sus órdenes (filtrable por símbolo) |
fills | Sí | Ejecuciones de sus operaciones (filtrable por símbolo) |
portfolio | Sí | Actualizaciones de sus posiciones y saldos |
liquidation | Sí | Cambios en su estado de liquidación |
rfq | Sí | Cotizaciones RFQ, actualizaciones de estado y notificaciones de ejecución |
experimental_order_updates, experimental_combo_fills | Sí | Canales experimentales de combos. Consulte API experimental de combos |
experimental_combo_trades | No | Operaciones públicas experimentales de combos |
Los datos de competiciones y clasificaciones no se sirven desde api.hypercall.xyz, ni por WebSocket ni por REST. Suscribirse a los canales retirados competition o competition_engagement devuelve un Error. La aplicación de trading lee las competiciones por HTTP desde el servicio de analítica en https://api-ui.hypercall.xyz, por ejemplo GET /v2/competitions, GET /v2/competitions/{id}, GET /v2/competitions/leaderboard y GET /v2/competitions/all-time/leaderboard.
Tipos de mensaje
Colocar orden (autenticado)
Coloque una orden a través de la ruta de comandos de 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..."
}
El nonce anterior es ilustrativo. Genere uno nuevo para cada orden a partir de la hora Unix actual en milisegundos, por ejemplo Date.now() en JavaScript. Un valor fijo copiado queda fuera de la ventana aceptada y se rechaza.
| Campo | Tipo | Descripción |
|---|---|---|
wallet | string | Dirección de la wallet propietaria de la orden |
symbol | string | Símbolo de la opción |
side | string | "Buy" o "Sell" |
size | string | Tamaño del contrato, idéntico al valor firmado |
price | string | Precio límite, idéntico al valor firmado |
tif | string | Time-in-force opcional, por defecto "gtc" |
route | string | Use "book_only". Una ruta omitida todavía se acepta, pero está obsoleta y su fecha de retirada ya ha pasado. |
client_id | string | ID de orden del cliente, opcional |
reduce_only | boolean | Opcional, por defecto false. Una orden reduce-only firma PlaceOrderReduceOnly y requiere un route explícito |
mmp_enabled | boolean | Opcional, por defecto false |
builder_code_address | string | Wallet de builder code, opcional |
nonce | integer | Nonce de firma obligatorio: la hora Unix actual en milisegundos. Debe estar dentro de un rango de dos días antes y un día después de la hora actual. Cada nonce se acepta una sola vez por firmante, y las órdenes REST y WebSocket comparten el mismo espacio de nonces. Un frame reproducido se rechaza. |
signature | string | Firma EIP-712 de PlaceOrder |
La conexión debe haber enviado Authenticate previamente; de lo contrario, el servidor responde {"type": "Error", "message": "Not authenticated"}. El firmante debe ser wallet o un agente aprobado para ella.
Actualmente, PlaceOrder por WebSocket se envía directamente al libro de órdenes. route="best_execution" y route="rfq_only" se rechazan en WebSocket porque esta ruta todavía no ejecuta el enrutamiento RPI/RFQ. Use POST /order para best_execution.
Una orden admitida recibe respuesta en la misma conexión con un OrderResult:
{
"type": "OrderResult",
"order_id": 12345,
"status": "ACKED",
"symbol": "BTC-20260131-100000-C",
"side": "Buy",
"price": "100",
"size": "1"
}
Se añade reason cuando la orden no es aceptada. Los fallos de validación, de firma y de límite de frecuencia se envían como un Error cuyo message empieza por Order placement failed:. Los cambios de estado posteriores llegan por order_updates.
Actualización del libro de órdenes
Snapshot/actualización L2 del libro de órdenes para un 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 | Descripción |
|---|---|---|
symbol | string | Símbolo de la opción |
option_token_address | string? | Dirección on-chain del token de la opción, cuando se conoce |
bids | array | Niveles bid como tuplas [price, size]; el tamaño está en contratos legibles |
asks | array | Niveles ask como tuplas [price, size]; el tamaño está en contratos legibles |
timestamp | integer | Marca de tiempo Unix (milisegundos) |
Operación
Evento público de operación.
{
"type": "Trade",
"symbol": "BTC-20260131-100000-C",
"price": "0.0523",
"size": "5.0",
"side": "Buy",
"timestamp": 1737331200000
}
| Campo | Tipo | Descripción |
|---|---|---|
symbol | string | Símbolo de la opción |
price | string | Precio de la operación en USD |
size | string | Tamaño de la operación en contratos |
side | string | Lado del agresor (Buy o Sell) |
timestamp | integer | Marca de tiempo Unix (milisegundos) |
Ejecución (autenticado)
Notificación de ejecución de su operación.
{
"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 | Descripción |
|---|---|---|
order_id | integer | ID de su orden |
fill_id | integer | ID de la ejecución |
symbol | string | Símbolo de la opción |
side | string | Su lado de la operación (Buy o Sell) |
price | string | Precio de ejecución en USD |
size | string | Tamaño de la ejecución en contratos |
timestamp | integer | Marca de tiempo Unix (milisegundos) |
wallet_address | string | Dirección de su wallet |
fee | string | Comisión de trading cobrada. Devuelve 0 mientras las comisiones del venue de lanzamiento estén deshabilitadas |
trade_id | integer | ID único de la operación |
is_taker | boolean | Indica si usted fue el taker |
builder_code_address | string? | Wallet de builder code (si existe) |
builder_code_fee | string? | Comisión de builder code. Devuelve null mientras las comisiones del venue de lanzamiento estén deshabilitadas |
instrument_type | string | "option" |
Actualización del portafolio (autenticado)
Se envía en el canal portfolio para posiciones, saldos, margen y griegas. Al suscribirse, el servidor envía un snapshot Initial y después actualizaciones incrementales.
El type de cada frame es su tipo de actualización:
| Tipo de actualización | Campos además 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 |
El timestamp de los frames de portfolio está en segundos Unix, a diferencia de las marcas de tiempo en milisegundos de los demás canales.
Ejemplo de actualización de griegas:
{
"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 portafolios vacíos, las actualizaciones de griegas usan:
per_leg: []aggregate: null
Actualización de orden (autenticado)
Notificación de cambio de estado de una orden. Se incluyen las actualizaciones PARTIALLY_FILLED.
filled_size es la cantidad acumulada ejecutada en todas las ejecuciones de la orden,
no la cantidad de la última ejecución.
{
"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 es uno de ACKED, OPEN, PARTIALLY_FILLED, FILLED, CANCELED o REJECTED. client_id se omite cuando la orden no tiene uno. instrument_type es "option" o "perp".
Actualización de mercado
Cambios en los listados de mercados.
Mercado creado:
{
"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 es Created, Expired, Deleted (solo incluye symbol y timestamp) o ComboCreated (consulte API experimental de combos).
Actualización de la cadena de opciones
Se envía en options_chain cuando cambia un listado o su mejor nivel del libro. action es Upsert o 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
}
Una fila Upsert contiene una pata (call o put) con los campos de pata del snapshot de la cadena de opciones: symbol, option_token_address, precio bid y ask, IV, tamaño en contratos y nocional en USD, greeks_abs y greeks_cash (el ejemplo anterior está abreviado). Los valores no disponibles son null; la pata que no forma parte de la actualización se omite. Un Remove incluye currency, expiry, strike, option_type, symbol y timestamp cuando un instrumento se elimina, vence o entra en liquidación al vencimiento pendiente. expiry está en segundos Unix.
Posición vencida (autenticado)
Se envía en el canal portfolio cuando su posición se liquida al vencimiento.
{
"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 y net_pnl son null cuando no están disponibles.
Cambio de estado de liquidación (autenticado)
Se envía en el canal liquidation cuando cambia el estado de liquidación de su cuenta.
{
"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 | Descripción |
|---|---|
previous_state, new_state | healthy, pre_liquidation, in_liquidation o liquidated |
liquidation_mode | partial, full, o null cuando no hay ningún flujo de liquidación activo |
margin_mode | standard o portfolio |
equity | Patrimonio de la cuenta |
mm_required | Requisito de margen de mantenimiento |
maintenance_margin | Patrimonio menos el requisito de margen de mantenimiento |
shortfall | Importe por el que la cuenta está por debajo del margen de mantenimiento |
partial_liquidation | Progreso de la liquidación parcial (hora de entrada, patrimonio objetivo, plazo de escalado, órdenes de liquidación activas, bonificación), o null |
full_liquidation | Detalles de la subasta de liquidación total (ID de subasta, hora de inicio, margen necesario y, al completarse, el ganador, la bonificación y la transacción de resolución), o null |
Consulte Liquidación para saber qué significa cada estado.
Actualización de precios índice
Precios spot/índice agrupados para todos los activos subyacentes.
{
"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 | Descripción |
|---|---|---|
prices | array | Array de entradas {underlying, price, timestamp} para cada activo subyacente seguido |
prices[].underlying | string | Símbolo del activo subyacente (p. ej., "BTC", "ETH") |
prices[].price | string | Precio spot/índice actual en USD |
prices[].timestamp | integer | Hora de observación en la fuente para este activo subyacente, en milisegundos Unix |
timestamp | integer | Hora de observación en la fuente más antigua incluida en prices, en milisegundos Unix |
Datos de mercado indicativos
Stream de proveedores de cotizaciones en lista de permitidos, con el mejor bid/ask agregado de los proveedores de cotizaciones registrados. Este canal aún no está disponible de forma general. Use los datos de mercado REST y los canales autenticados de órdenes/ejecuciones/portafolio, salvo que Hypercall haya habilitado el streaming de proveedores de cotizaciones para su integración.
{
"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 | Descripción |
|---|---|---|
instrument | string | Símbolo de la opción |
best_bid | string | Mejor precio bid agregado, opcional |
best_ask | string | Mejor precio ask agregado, opcional |
bid_iv | number | Volatilidad implícita del mejor bid, opcional |
ask_iv | number | Volatilidad implícita del mejor ask, opcional |
indicative_bid_size | string | Tamaño bid total entre proveedores, opcional |
indicative_ask_size | string | Tamaño ask total entre proveedores, opcional |
num_providers | integer | Número de proveedores de cotizaciones activos |
rfq_provider_quotes | array | Cotizaciones por proveedor, opcional: wallet, bid_price, ask_price, max_bid_size, max_ask_size, updated_at |
published_at | integer | Hora del reloj de la API en que se publicó esta fila, en milisegundos Unix. Mida la antigüedad de rfq_provider_quotes respecto a este valor, no respecto a su reloj local |
timestamp | integer | Hora de la última cotización del proveedor, en milisegundos Unix |
Lote de datos de mercado indicativos
Se envía en el canal indicative_market_data_v2 (consulte Entrega por lotes). Cada elemento de updates tiene los mismos campos que un mensaje IndicativeMarketData, sin 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 | Descripción |
|---|---|---|
flushed_at | integer | Hora del reloj de la API en que se envió el lote, en milisegundos Unix. flushed_at - published_at es el retraso por agrupamiento de una fila |
updates | array | Filas publicadas durante la ventana, de la más antigua a la más reciente |
Cotizaciones RFQ (autenticado)
Cotizaciones recibidas en respuesta a su solicitud 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"
}
Actualización de estado de RFQ (autenticado)
Cambio de estado de un RFQ que usted envió.
{
"type": "RfqStatusUpdate",
"rfq_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "executed",
"taker_wallet": "0x1234...abcd"
}
Sincronización de reloj
Envíe ClockSync con cualquier cadena nonce para obtener una muestra del reloj de la API en la misma conexión, por ejemplo para acotar el retraso de transporte:
{"type": "ClockSync", "nonce": "abc"}
{"type": "ClockSynced", "nonce": "abc", "server_at": 1737331200000}
server_at está en milisegundos Unix. No requiere autenticación.
Error
Mensaje de error del servidor. Base su lógica en code cuando esté presente, no en message.
{
"type": "Error",
"message": "Unknown channel 'foobar'",
"code": "unknown_channel",
"channel": "foobar"
}
| Campo | Tipo | Descripción |
|---|---|---|
message | string | Descripción legible |
code | string | Motivo estable, opcional. unknown_channel: un Subscribe indicó un canal que no existe |
channel | string | Opcional. El canal indicado por un Subscribe rechazado |
Un Subscribe a un canal que no figura en Canales disponibles recibe el error unknown_channel, y no se crea ninguna suscripción. Los servidores anteriores confirmaban cualquier nombre y luego no enviaban nada.
Los demás errores solo incluyen message. Ejemplos: Authentication required for this channel cuando se suscribe a un canal autenticado antes de Authenticate, Not authenticated para PlaceOrder antes de Authenticate, e Invalid wallet address: ....
Autenticación
Los canales autenticados requieren un mensaje de identificación de la wallet después de conectarse:
{"type": "Authenticate", "wallet": "0x1234567890abcdef..."}
Los mensajes de los canales autenticados se filtran para mostrar solo los datos de su wallet. Las conexiones WebSocket no requieren firma.
Ejemplo: cliente en 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())
Ejemplo: cliente en 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`);
}
};
Ejemplo: cliente en Rust
El WsClient del crate hypercall-client identifica la wallet, restaura las suscripciones tras una reconexión y responde por usted a los pings del servidor. Ofrece compresión permessage-deflate por defecto y recurre a frames sin comprimir cuando el servidor no la acepta, con los mismos mensajes JSON en ambos casos. Llame a with_permessage_deflate(false) para dejar de ofrecerla.
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(())
}