API WebSocket
Flux de données en temps réel pour le trading d'options sur Hypercall.
Consultez la référence interactive de l'API WebSocket pour une navigation plus confortable, avec des exemples en direct et le détail des schémas.
Téléchargez la spécification AsyncAPI pour un usage programmatique.
Les objets de message WebSocket peuvent recevoir de nouveaux champs sans préavis. Les clients doivent ignorer les champs qu'ils ne reconnaissent pas. Hypercall ne supprime aucun champ d'un schéma de message existant.
Connexion
Connectez-vous à wss://HOST/ws :
Points de terminaison :
- Production :
wss://api.hypercall.xyz/ws - Local :
ws://localhost:3000/ws
Le testnet est temporairement désactivé, le temps qu'Hypercall obtienne davantage de HYPE de testnet.
Compression
/ws prend en charge permessage-deflate (RFC 7692). Activez-le dans votre client pour réduire la bande passante des données de marché : sur du trafic de production capturé, les trames compressées indépendamment sont environ 45 % plus petites, et les lots de prix d'indice environ 70 % plus petits.
- Votre client propose l'extension dans la requête d'upgrade, par exemple
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits. - Lorsque le serveur l'accepte, la réponse
101contientSec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover. Chaque message du serveur est alors compressé séparément, sans fenêtre glissante partagée, et les messages de moins de quelques centaines d'octets restent envoyés sans compression. Votre bibliothèque décompresse de manière transparente. - Si la réponse ne contient pas d'en-tête
Sec-WebSocket-Extensions, toutes les trames sont non compressées. La compression est activée par environnement, les clients doivent donc gérer les deux cas ; les messages JSON sont identiques dans les deux cas. La production n'accepte pas encore l'extension, les trames de production sont donc actuellement non compressées. - Votre client peut compresser ce qu'il envoie. Le serveur décompresse chaque message client jusqu'à 256 Kio et ferme la connexion si un message dépasse cette limite.
Les navigateurs négocient l'extension automatiquement. La plupart des bibliothèques ne nécessitent aucune modification :
Python (websockets active la compression par défaut) :
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 : le WsClient de hypercall-client négocie permessage-deflate par défaut (voir Exemple : client Rust). tungstenite et tokio-tungstenite seuls ne l'implémentent pas encore et se connectent donc sans compression. Pour utiliser la compression avec votre propre socket, choisissez un client qui l'implémente, comme 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?;
Identification du portefeuille
Pour recevoir les données des canaux authentifiés (ordres, exécutions, portefeuille), identifiez votre portefeuille après la connexion en envoyant un message Authenticate :
{"type": "Authenticate", "wallet": "0x1234..."}
Le serveur répond par une confirmation :
{"type": "Authenticated", "wallet": "0x1234..."}
Après réception de Authenticated, vous pouvez vous abonner aux canaux authentifiés. Si l'adresse du portefeuille est invalide, le serveur répond par un message Error et la connexion reste ouverte.
Le paramètre de requête ?wallet= reste pris en charge pour la rétrocompatibilité, mais il est obsolète et sera supprimé dans une prochaine version. Privilégiez l'approche par message décrite ci-dessus.
Maintien de la connexion
Le serveur impose un heartbeat WebSocket :
- Il envoie une trame de contrôle
Pingtoutes les 20 secondes - Il attend un
Pongcorrespondant dans un délai de 60 secondes - Il ferme la connexion avec le code de fermeture
1008et le motifpong timeoutsi le client cesse de répondre
Les implémentations WebSocket des navigateurs gèrent le ping/pong automatiquement. De nombreuses bibliothèques WebSocket Rust, dont tungstenite et tokio-tungstenite, gèrent également le ping/pong des trames de contrôle à votre place. Consultez la documentation de la bibliothèque de votre client avant d'ajouter une gestion manuelle des Pong. Les implémentations personnalisées ou sur socket brut doivent répondre aux trames Ping par un Pong.
Récupération d'un consommateur lent
Le serveur ferme une connexion /ws qui ne parvient pas à écouler ses données sortantes dans les limites de sécurité configurées en nombre de messages, en octets encodés, en ancienneté de file d'attente ou en délai d'écriture sur le socket. Lorsque la connexion peut encore accepter une trame de fermeture, le serveur utilise le code 1008 et un motif JSON compact :
{"error":"slow_consumer","class":"ordered_public","cause":"message_age","recovery":"snapshot_resubscribe"}
Les champs du motif sont :
| Champ | Signification |
|---|---|
class | Classe de diffusion dont la trame a franchi la limite de sécurité. |
cause | message_limit, byte_limit, message_age ou write_timeout. |
recovery | Action requise ensuite, par exemple resubscribe, snapshot_resubscribe, portfolio_refetch ou rest_reconcile. |
Après toute déconnexion, reconnectez-vous, identifiez à nouveau le portefeuille si nécessaire, réabonnez-vous et réconciliez l'état courant avant de traiter de nouveaux événements. Les canaux publics ordonnés exigent un nouveau snapshot. Les canaux d'événements privés exigent une réconciliation via l'interface REST de référence, car la relecture par curseur n'est pas encore disponible. Une connexion totalement bloquée peut se terminer avant de pouvoir lire le motif de fermeture ; les clients doivent donc aussi appliquer ce processus de récupération en cas de fermeture non propre.
Utilisez des connexions distinctes pour les données de marché publiques à haut débit et pour les commandes authentifiées ou les flux privés. Les classes de diffusion déterminent les métriques et le comportement de récupération, mais les trames d'une même connexion partagent un seul chemin d'écriture ordonné sur le socket. Une écriture publique bloquée peut donc retarder les trames privées suivantes sur cette même connexion jusqu'à ce que le délai d'écriture la ferme.
S'abonner aux canaux
Envoyez un message JSON pour vous abonner :
{"type": "Subscribe", "channel": "orderbook"}
Pour vous désabonner :
{"type": "Unsubscribe", "channel": "orderbook"}
Vous recevrez une confirmation :
{"type": "Subscribed", "channel": "orderbook"}
Filtrage par symbole
Les canaux order_updates et fills acceptent un filtre symbols facultatif. Lorsqu'il est fourni, le serveur n'envoie que les messages dont le sous-jacent correspond à l'un des symboles indiqués.
{"type": "Subscribe", "channel": "order_updates", "symbols": ["BTC"]}
Les sous-jacents seuls ("BTC") comme les noms d'instruments complets ("BTC-20260131-100000-C") sont acceptés. Pour ajouter des symboles, envoyez un nouveau Subscribe. Pour retirer des symboles précis :
{"type": "Unsubscribe", "channel": "order_updates", "symbols": ["BTC"]}
Lorsqu'aucun symbols n'est indiqué, toutes les mises à jour concernant votre portefeuille sont transmises.
Les canaux publics orderbook, trades, indicative_market_data et indicative_market_data_v2 acceptent également symbols, comparé aux symboles d'instruments exacts (sans tenir compte de la casse), et non aux sous-jacents :
{"type": "Subscribe", "channel": "orderbook", "symbols": ["BTC-20260131-100000-C"]}
Sans symbols, ces canaux diffusent tous les instruments.
Filtrage de la chaîne d'options
Le canal options_chain permet de filtrer par symboles d'instruments, date d'échéance et type d'option :
{
"type": "Subscribe",
"channel": "options_chain",
"symbols": ["BTC-20260131-100000-C"],
"expiry": "2026-01-31",
"option_type": "call"
}
| Filtre | Valeurs | Par défaut |
|---|---|---|
symbols | Tableau de symboles d'instruments complets (par ex. ["BTC-20260131-100000-C"]) | Tous les instruments |
expiry | Chaîne de date "YYYY-MM-DD", comparée à la date calendaire UTC de l'échéance de chaque instrument | Toutes les échéances |
option_type | "call", "put", "both", ou omis pour les deux | Les deux |
Diffusion par lots
indicative_market_data_v2 transporte les mêmes lignes que indicative_market_data, avec plusieurs lignes par trame :
{"type": "Subscribe", "channel": "indicative_market_data_v2", "symbols": ["BTC-20260131-100000-C"]}
Le serveur confirme avec un marqueur de lot :
{"type": "Subscribed", "channel": "indicative_market_data_v2", "batch": true}
Il conserve ensuite chaque ligne pendant au plus une fenêtre de lot, 25 ms par défaut, et envoie les lignes publiées dans cette fenêtre sous la forme d'une seule trame IndicativeMarketDataBatch. Un lot est aussi envoyé par anticipation dès qu'il atteint 256 lignes.
- Détection de la prise en charge : seule une confirmation contenant
"batch": truesignifie que le serveur fonctionne par lots. Les serveurs publiés avant ce canal confirment n'importe quel nom de canal sans le marqueur puis n'envoient rien, et les serveurs actuels répondent à un canal inconnu par une erreurunknown_channel. Dans les deux cas, désabonnez-vous deindicative_market_data_v2et abonnez-vous àindicative_market_datasur la même connexion. - Latence : une ligne arrive jusqu'à une fenêtre plus tard que sur
indicative_market_data. Sur le trafic de production avec la fenêtre par défaut, les lignes ont attendu 12 ms en moyenne et jamais plus de 25 ms. Utilisez le canal v1 si vous réagissez à des mises à jour de cotation individuelles en quelques millisecondes. - Bande passante : le regroupement par lots seul économise environ 7 % des octets. Le gain important vient avec
permessage-deflate: les lots compressés sont environ 9,8 fois plus petits que des trames non compressées d'une ligne pour un abonnement non filtré, et environ 8,3 fois plus petits pour une seule chaîne d'options, contre environ 1,8 fois pour des lignes compressées une à une. - Ordre : les lignes de
updatessont dans l'ordre de publication, et les lots arrivent dans l'ordre. Les lignes ne sont pas fusionnées, un même instrument peut donc apparaître plusieurs fois dans un lot. Appliquez les lignes dans l'ordre pour obtenir le même état que sur le canal v1. - Snapshot : le snapshot envoyé à l'abonnement arrive lui aussi sous forme de trames de lot.
- Filtres :
symbolsfonctionne comme surindicative_market_data. Le fait de restreindre le filtre ou de se désabonner envoie d'abord les lignes déjà en attente sous l'ancien filtre, puis la confirmationUnsubscribed, de sorte qu'aucune ligne n'est perdue ni dupliquée. - Canaux indépendants : une connexion peut s'abonner aux deux canaux avec des filtres différents.
indicative_market_datareste inchangé.
Canaux disponibles
| Canal | Authentification requise | Description |
|---|---|---|
orderbook | Non | Mises à jour L2 du carnet d'ordres pour tous les symboles |
trades | Non | Flux public des transactions |
market_updates | Non | Changements de cotation des marchés (création/suppression/expiration) |
options_chain | Non | Mises à jour incrémentales de la chaîne d'options (filtrables par symbols, expiry, option_type) |
index_prices | Non | Prix spot/d'indice en temps réel pour tous les sous-jacents |
indicative_market_data | Non | Flux de fournisseurs de cotations sur liste d'autorisation. Pas encore disponible pour tous |
indicative_market_data_v2 | Non | Le même flux en trames de lot. Voir Diffusion par lots |
order_updates | Oui | Changements de statut de vos ordres (filtrables par symbole) |
fills | Oui | Exécutions de vos transactions (filtrables par symbole) |
portfolio | Oui | Mises à jour de vos positions et de votre solde |
liquidation | Oui | Changements de votre état de liquidation |
rfq | Oui | Cotations RFQ, mises à jour de statut et notifications d'exécution |
experimental_order_updates, experimental_combo_fills | Oui | Canaux combo expérimentaux. Voir API Combo expérimentale |
experimental_combo_trades | Non | Transactions combo publiques expérimentales |
Les données de compétition et de classement ne sont pas servies par api.hypercall.xyz, ni en WebSocket ni en REST. S'abonner aux canaux retirés competition ou competition_engagement renvoie une Error. L'application de trading lit les compétitions en HTTP depuis le service d'analytique à https://api-ui.hypercall.xyz, par exemple GET /v2/competitions, GET /v2/competitions/{id}, GET /v2/competitions/leaderboard et GET /v2/competitions/all-time/leaderboard.
Types de messages
Passer un ordre (authentifié)
Passez un ordre via le chemin de commande 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..."
}
Le nonce ci-dessus est donné à titre d'illustration. Générez-en un nouveau pour chaque ordre à partir de l'heure Unix courante en millisecondes, par exemple Date.now() en JavaScript. Une valeur fixe recopiée sort de la fenêtre acceptée et est rejetée.
| Champ | Type | Description |
|---|---|---|
wallet | string | Adresse du portefeuille propriétaire de l'ordre |
symbol | string | Symbole de l'option |
side | string | "Buy" ou "Sell" |
size | string | Taille du contrat, identique à la valeur signée |
price | string | Prix limite, identique à la valeur signée |
tif | string | Durée de validité facultative, "gtc" par défaut |
route | string | Utilisez "book_only". Une route omise est encore acceptée mais obsolète, et sa date de fin de prise en charge est dépassée. |
client_id | string | Identifiant d'ordre client facultatif |
reduce_only | boolean | Facultatif, false par défaut. Un ordre reduce-only signe PlaceOrderReduceOnly et exige une route explicite |
mmp_enabled | boolean | Facultatif, false par défaut |
builder_code_address | string | Portefeuille de builder code facultatif |
nonce | integer | Nonce de signature obligatoire : l'heure Unix courante en millisecondes. Il doit se situer entre deux jours avant et un jour après l'heure courante. Chaque nonce n'est accepté qu'une fois par signataire, et les ordres REST et WebSocket partagent le même espace de nonces. Une trame rejouée est rejetée. |
signature | string | Signature EIP-712 PlaceOrder |
La connexion doit avoir envoyé Authenticate au préalable ; sinon, le serveur répond {"type": "Error", "message": "Not authenticated"}. Le signataire doit être wallet ou un agent approuvé pour ce portefeuille.
Le PlaceOrder WebSocket est actuellement transmis directement au carnet d'ordres. route="best_execution" et route="rfq_only" sont rejetés en WebSocket, car ce chemin n'exécute pas encore le routage RPI/RFQ. Utilisez POST /order pour best_execution.
Un ordre admis reçoit une réponse OrderResult sur la même connexion :
{
"type": "OrderResult",
"order_id": 12345,
"status": "ACKED",
"symbol": "BTC-20260131-100000-C",
"side": "Buy",
"price": "100",
"size": "1"
}
reason est ajouté lorsque l'ordre n'est pas accepté. Les échecs de validation, de signature et de limite de débit sont envoyés sous forme d'Error dont le message commence par Order placement failed:. Les changements de statut ultérieurs arrivent sur order_updates.
Mise à jour du carnet d'ordres
Snapshot/mise à jour L2 du carnet d'ordres pour un symbole.
{
"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
}
| Champ | Type | Description |
|---|---|---|
symbol | string | Symbole de l'option |
option_token_address | string? | Adresse on-chain du jeton d'option, lorsqu'elle est connue |
bids | array | Niveaux bid sous forme de tuples [price, size], la taille est exprimée en contrats lisibles |
asks | array | Niveaux ask sous forme de tuples [price, size], la taille est exprimée en contrats lisibles |
timestamp | integer | Horodatage Unix (millisecondes) |
Transaction
Événement de transaction public.
{
"type": "Trade",
"symbol": "BTC-20260131-100000-C",
"price": "0.0523",
"size": "5.0",
"side": "Buy",
"timestamp": 1737331200000
}
| Champ | Type | Description |
|---|---|---|
symbol | string | Symbole de l'option |
price | string | Prix de la transaction en USD |
size | string | Taille de la transaction en contrats |
side | string | Côté de l'agresseur (Buy ou Sell) |
timestamp | integer | Horodatage Unix (millisecondes) |
Exécution (authentifié)
Notification d'exécution de votre transaction.
{
"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"
}
| Champ | Type | Description |
|---|---|---|
order_id | integer | Identifiant de votre ordre |
fill_id | integer | Identifiant de l'exécution |
symbol | string | Symbole de l'option |
side | string | Votre côté de la transaction (Buy ou Sell) |
price | string | Prix d'exécution en USD |
size | string | Taille exécutée en contrats |
timestamp | integer | Horodatage Unix (millisecondes) |
wallet_address | string | Adresse de votre portefeuille |
fee | string | Frais de trading facturés. Renvoie 0 tant que les frais de la plateforme sont désactivés pour le lancement |
trade_id | integer | Identifiant unique de la transaction |
is_taker | boolean | Indique si vous étiez le taker |
builder_code_address | string? | Portefeuille de builder code (le cas échéant) |
builder_code_fee | string? | Frais de builder code. Renvoie null tant que les frais de la plateforme sont désactivés pour le lancement |
instrument_type | string | "option" |
Mise à jour du portefeuille (authentifié)
Envoyée sur le canal portfolio pour les positions, les soldes, la marge et les grecques. À l'abonnement, le serveur envoie un snapshot Initial, puis des mises à jour incrémentales.
Le type de chaque trame est son type de mise à jour :
| Type de mise à jour | Champs en plus 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 |
Le timestamp des trames portfolio est exprimé en secondes Unix, contrairement aux horodatages en millisecondes des autres canaux.
Exemple de mise à jour des grecques :
{
"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
}
}
Pour les portefeuilles vides, les mises à jour des grecques utilisent :
per_leg: []aggregate: null
Mise à jour d'ordre (authentifié)
Notification de changement de statut d'un ordre. Les mises à jour PARTIALLY_FILLED sont incluses.
filled_size est la quantité cumulée exécutée sur l'ensemble des exécutions de l'ordre,
et non la quantité de la dernière exécution.
{
"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 vaut ACKED, OPEN, PARTIALLY_FILLED, FILLED, CANCELED ou REJECTED. client_id est omis lorsque l'ordre n'en a pas. instrument_type vaut "option" ou "perp".
Mise à jour de marché
Changements de cotation des marchés.
Marché créé :
{
"type": "MarketUpdate",
"action": "Created",
"symbol": "BTC-20260131-100000-C",
"strike": "100000",
"is_call": true,
"underlying": "BTC",
"expiry": 1738281600,
"timestamp": 1737331200000
}
Marché expiré :
{
"type": "MarketUpdate",
"action": "Expired",
"symbol": "BTC-20260131-100000-C",
"strike": "100000",
"is_call": true,
"underlying": "BTC",
"expiry": 1738281600,
"timestamp": 1738281600000
}
action vaut Created, Expired, Deleted (ne contient que symbol et timestamp) ou ComboCreated (voir API Combo expérimentale).
Mise à jour de la chaîne d'options
Envoyée sur options_chain lorsqu'une cotation ou son meilleur niveau du carnet change. action vaut 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
}
Une ligne Upsert contient une seule jambe (call ou put) avec les champs de jambe du snapshot de la chaîne d'options : symbol, option_token_address, prix bid et ask, IV, taille en contrats et notionnel en USD, greeks_abs et greeks_cash (l'exemple ci-dessus est abrégé). Les valeurs indisponibles valent null ; la jambe qui ne fait pas partie de la mise à jour est omise. Un Remove contient currency, expiry, strike, option_type, symbol et timestamp lorsqu'un instrument est supprimé, arrive à échéance ou passe en attente de règlement. expiry est exprimé en secondes Unix.
Position expirée (authentifié)
Envoyée sur le canal portfolio lorsque votre position est réglée à l'échéance.
{
"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 et net_pnl valent null lorsqu'ils ne sont pas disponibles.
Changement d'état de liquidation (authentifié)
Envoyé sur le canal liquidation lorsque l'état de liquidation de votre compte change.
{
"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
}
| Champ | Description |
|---|---|
previous_state, new_state | healthy, pre_liquidation, in_liquidation ou liquidated |
liquidation_mode | partial, full, ou null lorsqu'aucun processus de liquidation n'est actif |
margin_mode | standard ou portfolio |
equity | Valeur nette du compte |
mm_required | Exigence de marge de maintenance |
maintenance_margin | Valeur nette moins l'exigence de marge de maintenance |
shortfall | Montant manquant pour que le compte atteigne la marge de maintenance |
partial_liquidation | Avancement de la liquidation partielle (heure d'entrée, valeur nette cible, échéance d'escalade, ordres de liquidation actifs, bonus), ou null |
full_liquidation | Détails de l'enchère de liquidation totale (identifiant d'enchère, heure de début, marge nécessaire et, à l'issue, le gagnant, le bonus et la transaction de résolution), ou null |
Consultez Liquidation pour la signification de chaque état.
Mise à jour des prix d'indice
Prix spot/d'indice regroupés pour tous les sous-jacents.
{
"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
}
| Champ | Type | Description |
|---|---|---|
prices | array | Tableau d'entrées {underlying, price, timestamp} pour chaque sous-jacent suivi |
prices[].underlying | string | Symbole du sous-jacent (par ex. "BTC", "ETH") |
prices[].price | string | Prix spot/d'indice courant en USD |
prices[].timestamp | integer | Heure d'observation à la source pour ce sous-jacent, en millisecondes Unix |
timestamp | integer | Heure d'observation à la source la plus ancienne incluse dans prices, en millisecondes Unix |
Données de marché indicatives
Flux de fournisseurs de cotations sur liste d'autorisation, avec les meilleurs bid/ask agrégés des fournisseurs de cotations enregistrés. Ce canal n'est pas encore disponible pour tous. Utilisez les données de marché REST et les canaux authentifiés d'ordres, d'exécutions et de portefeuille, sauf si Hypercall a activé le flux des fournisseurs de cotations pour votre intégration.
{
"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
}
| Champ | Type | Description |
|---|---|---|
instrument | string | Symbole de l'option |
best_bid | string | Meilleur prix bid agrégé, facultatif |
best_ask | string | Meilleur prix ask agrégé, facultatif |
bid_iv | number | Volatilité implicite du meilleur bid, facultative |
ask_iv | number | Volatilité implicite du meilleur ask, facultative |
indicative_bid_size | string | Taille bid totale sur l'ensemble des fournisseurs, facultative |
indicative_ask_size | string | Taille ask totale sur l'ensemble des fournisseurs, facultative |
num_providers | integer | Nombre de fournisseurs de cotations actifs |
rfq_provider_quotes | array | Cotations par fournisseur, facultatives : wallet, bid_price, ask_price, max_bid_size, max_ask_size, updated_at |
published_at | integer | Heure de publication de cette ligne selon l'horloge de l'API, en millisecondes Unix. Mesurez l'ancienneté de rfq_provider_quotes par rapport à cette valeur, et non à votre horloge locale |
timestamp | integer | Heure de la dernière cotation d'un fournisseur, en millisecondes Unix |
Lot de données de marché indicatives
Envoyé sur le canal indicative_market_data_v2 (voir Diffusion par lots). Chaque élément de updates possède les mêmes champs qu'un message IndicativeMarketData, sans 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
}
]
}
| Champ | Type | Description |
|---|---|---|
flushed_at | integer | Heure d'envoi du lot selon l'horloge de l'API, en millisecondes Unix. flushed_at - published_at correspond au délai de regroupement d'une ligne |
updates | array | Lignes publiées pendant la fenêtre, de la plus ancienne à la plus récente |
Cotations RFQ (authentifié)
Cotations reçues en réponse à votre soumission 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"
}
Mise à jour de statut RFQ (authentifié)
Changement de statut d'une RFQ que vous avez soumise.
{
"type": "RfqStatusUpdate",
"rfq_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "executed",
"taker_wallet": "0x1234...abcd"
}
Synchronisation d'horloge
Envoyez ClockSync avec une chaîne nonce quelconque pour obtenir un échantillon de l'horloge de l'API sur la même connexion, par exemple pour borner le délai de transport :
{"type": "ClockSync", "nonce": "abc"}
{"type": "ClockSynced", "nonce": "abc", "server_at": 1737331200000}
server_at est exprimé en millisecondes Unix. Aucune authentification n'est nécessaire.
Erreur
Message d'erreur du serveur. Basez-vous sur code lorsqu'il est présent, et non sur message.
{
"type": "Error",
"message": "Unknown channel 'foobar'",
"code": "unknown_channel",
"channel": "foobar"
}
| Champ | Type | Description |
|---|---|---|
message | string | Description lisible |
code | string | Motif stable, facultatif. unknown_channel : un Subscribe a désigné un canal qui n'existe pas |
channel | string | Facultatif. Le canal désigné par un Subscribe rejeté |
Un Subscribe vers un canal absent de la liste des canaux disponibles reçoit l'erreur unknown_channel, et aucun abonnement n'est créé. Les serveurs plus anciens confirmaient n'importe quel nom puis n'envoyaient rien.
Les autres erreurs ne contiennent que message. Exemples : Authentication required for this channel lorsque vous vous abonnez à un canal authentifié avant Authenticate, Not authenticated pour un PlaceOrder avant Authenticate, et Invalid wallet address: ....
Authentification
Les canaux authentifiés exigent un message d'identification du portefeuille après la connexion :
{"type": "Authenticate", "wallet": "0x1234567890abcdef..."}
Les messages des canaux authentifiés sont filtrés pour n'afficher que les données de votre portefeuille. Aucune signature n'est requise pour les connexions WebSocket.
Exemple : client 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())
Exemple : client 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`);
}
};
Exemple : client Rust
Le WsClient du crate hypercall-client identifie le portefeuille, rétablit les abonnements après une reconnexion et répond aux pings du serveur à votre place. Il propose la compression permessage-deflate par défaut et se rabat sur des trames non compressées lorsque le serveur ne l'accepte pas, avec les mêmes messages JSON dans les deux cas. Appelez with_permessage_deflate(false) pour ne plus la proposer.
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(())
}