Cette page a été traduite automatiquement. La version anglaise fait référence. Lire en anglais →
Aller au contenu principal

API WebSocket

Flux de données en temps réel pour le trading d'options sur Hypercall.

Référence interactive

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.

Spécification lisible par machine

Téléchargez la spécification AsyncAPI pour un usage programmatique.

Évolution additive du schéma

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
État du testnet

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 101 contient Sec-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.

Obsolète : authentification par paramètre de requête

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 Ping toutes les 20 secondes
  • Il attend un Pong correspondant dans un délai de 60 secondes
  • Il ferme la connexion avec le code de fermeture 1008 et le motif pong timeout si 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 :

ChampSignification
classClasse de diffusion dont la trame a franchi la limite de sécurité.
causemessage_limit, byte_limit, message_age ou write_timeout.
recoveryAction 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"
}
FiltreValeursPar défaut
symbolsTableau de symboles d'instruments complets (par ex. ["BTC-20260131-100000-C"])Tous les instruments
expiryChaîne de date "YYYY-MM-DD", comparée à la date calendaire UTC de l'échéance de chaque instrumentToutes les échéances
option_type"call", "put", "both", ou omis pour les deuxLes 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": true signifie 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 erreur unknown_channel. Dans les deux cas, désabonnez-vous de indicative_market_data_v2 et abonnez-vous à indicative_market_data sur 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 updates sont 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 : symbols fonctionne comme sur indicative_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 confirmation Unsubscribed, 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_data reste inchangé.

Canaux disponibles​

CanalAuthentification requiseDescription
orderbookNonMises à jour L2 du carnet d'ordres pour tous les symboles
tradesNonFlux public des transactions
market_updatesNonChangements de cotation des marchés (création/suppression/expiration)
options_chainNonMises à jour incrémentales de la chaîne d'options (filtrables par symbols, expiry, option_type)
index_pricesNonPrix spot/d'indice en temps réel pour tous les sous-jacents
indicative_market_dataNonFlux de fournisseurs de cotations sur liste d'autorisation. Pas encore disponible pour tous
indicative_market_data_v2NonLe même flux en trames de lot. Voir Diffusion par lots
order_updatesOuiChangements de statut de vos ordres (filtrables par symbole)
fillsOuiExécutions de vos transactions (filtrables par symbole)
portfolioOuiMises à jour de vos positions et de votre solde
liquidationOuiChangements de votre état de liquidation
rfqOuiCotations RFQ, mises à jour de statut et notifications d'exécution
experimental_order_updates, experimental_combo_fillsOuiCanaux combo expérimentaux. Voir API Combo expérimentale
experimental_combo_tradesNonTransactions 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.

ChampTypeDescription
walletstringAdresse du portefeuille propriétaire de l'ordre
symbolstringSymbole de l'option
sidestring"Buy" ou "Sell"
sizestringTaille du contrat, identique à la valeur signée
pricestringPrix limite, identique à la valeur signée
tifstringDurée de validité facultative, "gtc" par défaut
routestringUtilisez "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_idstringIdentifiant d'ordre client facultatif
reduce_onlybooleanFacultatif, false par défaut. Un ordre reduce-only signe PlaceOrderReduceOnly et exige une route explicite
mmp_enabledbooleanFacultatif, false par défaut
builder_code_addressstringPortefeuille de builder code facultatif
nonceintegerNonce 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.
signaturestringSignature 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
}
ChampTypeDescription
symbolstringSymbole de l'option
option_token_addressstring?Adresse on-chain du jeton d'option, lorsqu'elle est connue
bidsarrayNiveaux bid sous forme de tuples [price, size], la taille est exprimée en contrats lisibles
asksarrayNiveaux ask sous forme de tuples [price, size], la taille est exprimée en contrats lisibles
timestampintegerHorodatage 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
}
ChampTypeDescription
symbolstringSymbole de l'option
pricestringPrix de la transaction en USD
sizestringTaille de la transaction en contrats
sidestringCôté de l'agresseur (Buy ou Sell)
timestampintegerHorodatage 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"
}
ChampTypeDescription
order_idintegerIdentifiant de votre ordre
fill_idintegerIdentifiant de l'exécution
symbolstringSymbole de l'option
sidestringVotre côté de la transaction (Buy ou Sell)
pricestringPrix d'exécution en USD
sizestringTaille exécutée en contrats
timestampintegerHorodatage Unix (millisecondes)
wallet_addressstringAdresse de votre portefeuille
feestringFrais de trading facturés. Renvoie 0 tant que les frais de la plateforme sont désactivés pour le lancement
trade_idintegerIdentifiant unique de la transaction
is_takerbooleanIndique si vous étiez le taker
builder_code_addressstring?Portefeuille de builder code (le cas échéant)
builder_code_feestring?Frais de builder code. Renvoie null tant que les frais de la plateforme sont désactivés pour le lancement
instrument_typestring"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 à jourChamps en plus de timestamp
Initialpositions
PositionUpdateposition
BalanceUpdatetotal_margin_used
MarginUpdatespan_margin, margin_summary, margin_mode, total_margin_used, available_balance
GreeksUpdateper_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
}
ChampDescription
previous_state, new_statehealthy, pre_liquidation, in_liquidation ou liquidated
liquidation_modepartial, full, ou null lorsqu'aucun processus de liquidation n'est actif
margin_modestandard ou portfolio
equityValeur nette du compte
mm_requiredExigence de marge de maintenance
maintenance_marginValeur nette moins l'exigence de marge de maintenance
shortfallMontant manquant pour que le compte atteigne la marge de maintenance
partial_liquidationAvancement de la liquidation partielle (heure d'entrée, valeur nette cible, échéance d'escalade, ordres de liquidation actifs, bonus), ou null
full_liquidationDé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
}
ChampTypeDescription
pricesarrayTableau d'entrées {underlying, price, timestamp} pour chaque sous-jacent suivi
prices[].underlyingstringSymbole du sous-jacent (par ex. "BTC", "ETH")
prices[].pricestringPrix spot/d'indice courant en USD
prices[].timestampintegerHeure d'observation à la source pour ce sous-jacent, en millisecondes Unix
timestampintegerHeure 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
}
ChampTypeDescription
instrumentstringSymbole de l'option
best_bidstringMeilleur prix bid agrégé, facultatif
best_askstringMeilleur prix ask agrégé, facultatif
bid_ivnumberVolatilité implicite du meilleur bid, facultative
ask_ivnumberVolatilité implicite du meilleur ask, facultative
indicative_bid_sizestringTaille bid totale sur l'ensemble des fournisseurs, facultative
indicative_ask_sizestringTaille ask totale sur l'ensemble des fournisseurs, facultative
num_providersintegerNombre de fournisseurs de cotations actifs
rfq_provider_quotesarrayCotations par fournisseur, facultatives : wallet, bid_price, ask_price, max_bid_size, max_ask_size, updated_at
published_atintegerHeure 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
timestampintegerHeure 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
}
]
}
ChampTypeDescription
flushed_atintegerHeure 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
updatesarrayLignes 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"
}
ChampTypeDescription
messagestringDescription lisible
codestringMotif stable, facultatif. unknown_channel : un Subscribe a désigné un canal qui n'existe pas
channelstringFacultatif. 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(())
}