API WebSocket
Diffusion de données en temps réel pour le trading d'options Hypercall.
Consultez la référence interactive de l'API WebSocket pour une meilleure expérience de navigation, avec des exemples en direct et des détails de schéma.
Téléchargez la spécification AsyncAPI pour un usage programmatique.
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é jusqu'à ce que Hypercall acquière davantage de HYPE de testnet.
Identification du portefeuille
Pour recevoir des données sur les 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 avoir reçu 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= est toujours pris en charge pour des raisons de rétrocompatibilité, mais il est obsolète et sera supprimé dans une prochaine version. Privilégiez l'approche par message décrite ci-dessus.
Vivacité de la connexion
Le serveur applique un heartbeat WebSocket :
- Envoie une trame de contrôle
Pingtoutes les 20 secondes - Attend un
Pongcorrespondant dans les 60 secondes - 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 pour vous. Consultez la documentation de la bibliothèque de votre client avant d'ajouter une gestion manuelle du Pong. Les implémentations personnalisées ou utilisant des sockets bruts doivent répondre aux trames Ping par un Pong.
Récupération des consommateurs lents
Le serveur ferme une connexion /ws qui ne parvient pas à écouler les données sortantes dans les limites de sécurité configurées pour les messages, les octets encodés, l'ancienneté de la file d'attente ou l'é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 les suivants :
| Champ | Signification |
|---|---|
class | Classe de livraison dont la trame a franchi la limite de sécurité. |
cause | message_limit, byte_limit, message_age ou write_timeout. |
recovery | Action requise à effectuer ensuite, telle que 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 actuel avant de traiter de nouveaux événements. Les canaux publics ordonnés nécessitent un nouveau snapshot. Les canaux d'événements privés nécessitent une réconciliation via la surface REST faisant autorité, 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, si bien que les clients doivent utiliser ce flux de récupération également 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 livraison déterminent les métriques et le comportement de récupération, mais les trames d'une même connexion partagent toujours un unique chemin d'écriture de socket ordonné. Une écriture publique bloquée peut donc retarder des trames privées ultérieures sur cette même connexion jusqu'à ce que le délai d'écriture la ferme.
Abonnement 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 prennent en charge un filtre symbols optionnel. Lorsqu'il est fourni, le serveur n'envoie que les messages dont le sous-jacent correspond à l'un des symboles spécifiés.
{"type": "Subscribe", "channel": "order_updates", "symbols": ["BTC"]}
Les sous-jacents simples ("BTC") et les noms d'instruments complets ("BTC-20260131-100000-C") sont tous deux acceptés. Pour ajouter d'autres symboles, envoyez un nouveau Subscribe. Pour supprimer des symboles spécifiques :
{"type": "Unsubscribe", "channel": "order_updates", "symbols": ["BTC"]}
Lorsqu'aucun symbols n'est spécifié, toutes les mises à jour de votre portefeuille sont transmises.
Filtrage de la chaîne d'options
Le canal options_chain prend en charge le filtrage par symboles sous-jacents, 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" | Toutes les échéances |
option_type | "call", "put", ou à omettre pour les deux | Les deux |
Canaux disponibles
| Canal | Authentification requise | Description |
|---|---|---|
orderbook | Non | Mises à jour du carnet d'ordres L2 pour tous les symboles |
trades | Non | Flux public des transactions |
market_updates | Non | Changements de cotation de marché (créé/supprimé/expiré) |
options_chain | Non | Mises à jour incrémentales de la chaîne d'options (filtrables par symboles, échéance, type d'option) |
index_prices | Non | Prix spot/index 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 de manière générale |
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 l'état de liquidation vous concernant |
competition | Oui | Récapitulatif de votre P&L de compétition, classement et statistiques finales |
competition_engagement | Oui | Changements de classement, écart avec le rang suivant et classements finaux |
rfq | Oui | Cotations RFQ, mises à jour de statut et notifications d'exécution |
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": 1000,
"signature": "0x..."
}
| 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, correspondant exactement à la valeur signée |
price | string | Prix limite, correspondant exactement à la valeur signée |
tif | string | Durée de validité optionnelle, "gtc" par défaut |
route | string | Route optionnelle. Utilisez "book_only" pour les ordres WebSocket sensibles au routage. Une route omise reste acceptée au moins jusqu'au 4 juillet 2026. |
client_id | string | ID d'ordre client optionnel |
nonce | integer | Nonce de signature unique |
signature | string | Signature EIP-712 PlaceOrder |
Le PlaceOrder WebSocket est actuellement acheminé directement vers le carnet d'ordres. route="best_execution" et route="rfq_only" sont rejetés sur WebSocket, car ce chemin n'exécute pas encore le routage RPI/RFQ. Utilisez POST /order pour best_execution.
Mise à jour du carnet d'ordres
Snapshot/mise à jour du carnet d'ordres L2 pour un symbole.
{
"type": "OrderbookUpdate",
"symbol": "BTC-20260131-100000-C",
"bids": [["95000.5", "10.5"], ["94999.0", "25.0"]],
"asks": [["95001.0", "8.0"], ["95002.5", "15.0"]],
"timestamp": 1737331200000
}
| Champ | Type | Description |
|---|---|---|
symbol | string | Symbole de l'option |
bids | array | Niveaux d'offre sous forme de tuples [price, size], la taille étant exprimée en contrats lisibles par un humain |
asks | array | Niveaux de demande sous forme de tuples [price, size], la taille étant exprimée en contrats lisibles par un humain |
timestamp | integer | Timestamp Unix (millisecondes) |
Transaction
Événement public de transaction.
{
"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é agresseur (buy ou sell) |
timestamp | integer | Timestamp 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
}
| Champ | Type | Description |
|---|---|---|
order_id | integer | ID de votre ordre |
fill_id | integer | ID de l'exécution |
symbol | string | Symbole de l'option |
side | string | Sens de la transaction (buy ou sell) |
price | string | Prix d'exécution en USD |
size | string | Taille de l'exécution en contrats |
timestamp | integer | Horodatage Unix (millisecondes) |
wallet_address | string | Adresse de votre portefeuille |
fee | string | Frais de transaction prélevés. Renvoie 0 tant que les frais de la venue de lancement sont désactivés |
trade_id | integer | ID unique de la transaction |
is_taker | boolean | Indique si vous étiez le taker |
builder_code_address | string? | Portefeuille du builder code (le cas échéant) |
builder_code_fee | string? | Frais du builder code. Renvoie null tant que les frais de la venue de lancement sont désactivés |
Mise à jour du portefeuille (authentifié)
Mise à jour du flux de portefeuille pour les positions, les soldes, la marge et les Greeks.
Exemple de mise à jour des Greeks :
{
"type": "PortfolioUpdate",
"timestamp": 1737331200000,
"per_leg": [
{
"symbol": "BTC-20260131-100000-C",
"quantity": "2.0",
"delta": 0.91,
"gamma": 0.003,
"theta": -0.12,
"vega": 0.44,
"iv": 0.63
}
],
"aggregate": {
"delta": 0.91,
"gamma": 0.003,
"theta": -0.12,
"vega": 0.44,
"iv": 0.63
}
}
Pour les portefeuilles vides, les mises à jour des Greeks utilisent :
per_leg: []aggregate: null
Récapitulatif du PnL de compétition (authentifié)
Mise à jour du flux de compétition pour l'affichage du PnL en en-tête/pied de page.
{
"type": "CompetitionPnlSummary",
"wallet_address": "0x1234...abcd",
"lifetime_realized_pnl": "1250.50",
"active_competition": {
"competition_id": 7,
"competition_name": "Spring Sprint",
"competition_state": "active",
"rank": 12,
"pnl": "420.25",
"volume": "25000",
"efficiency": "0.01681",
"medal": null
},
"timestamp": 1737331200000
}
Lorsqu'il n'y a aucune compétition active, active_competition vaut null.
Mise à jour d'ordre (authentifié)
Notification de changement de statut d'un ordre.
{
"type": "OrderUpdate",
"order_id": 12345,
"client_order_id": "my-order-1",
"status": "filled",
"filled_size": "10.0",
"remaining_size": "0",
"avg_fill_price": "0.0523"
}
Mise à jour du marché
Changements dans les listings de 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é arrivé à échéance :
{
"type": "MarketUpdate",
"action": "Expired",
"symbol": "BTC-20260131-100000-C",
"strike": "100000",
"is_call": true,
"underlying": "BTC",
"expiry": 1738281600,
"timestamp": 1738281600000
}
Position arrivée à échéance (authentifié)
Notification 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",
"timestamp": 1738281600000
}
Changement d'état de liquidation (authentifié)
Changement de l'état de liquidation de votre compte.
{
"type": "LiquidationStateChange",
"wallet_address": "0x1234...abcd",
"previous_state": "Normal",
"new_state": "Warning",
"equity": "10000.0",
"mm_required": "9500.0",
"shortfall": "0",
"auction_id": null,
"timestamp": 1737331200000
}
| État | Description |
|---|---|
Normal | Le compte est sain |
Warning | Approche d'un appel de marge |
Liquidating | Enchère de liquidation active |
Mise à jour du prix de l'indice
Prix spot/indice regroupés pour tous les sous-jacents.
{
"type": "IndexPriceUpdate",
"prices": [
{"underlying": "BTC", "price": "97250.50"},
{"underlying": "ETH", "price": "3200.00"},
{"underlying": "HYPE", "price": "28.50"}
],
"timestamp": 1737331200000
}
| Champ | Type | Description |
|---|---|---|
prices | array | Tableau d'entrées {underlying, price} pour chaque sous-jacent suivi |
prices[].underlying | string | Symbole du sous-jacent (par exemple, "BTC", "ETH") |
prices[].price | string | Prix spot/indice actuel en USD |
timestamp | integer | Horodatage Unix (millisecondes) |
Données de marché indicatives
Flux de fournisseurs de cotations sur liste blanche avec les meilleures offre/demande agrégées des fournisseurs de cotations enregistrés. Ce canal n'est pas encore disponible de manière générale. Utilisez les données de marché REST et les canaux authentifiés ordre/exécution/portefeuille, sauf si Hypercall a activé le streaming de 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,
"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 (facultatif) |
ask_iv | number | Volatilité implicite du meilleur ask (facultatif) |
indicative_bid_size | string | Taille bid totale sur l'ensemble des fournisseurs (facultatif) |
indicative_ask_size | string | Taille ask totale sur l'ensemble des fournisseurs (facultatif) |
num_providers | integer | Nombre de fournisseurs de cotations actifs |
timestamp | integer | Horodatage Unix (millisecondes) |
Changement de rang en compétition (authentifié)
Notification lorsque votre rang change dans une compétition active.
{
"type": "CompetitionRankChange",
"wallet_address": "0x1234...abcd",
"competition_id": 7,
"from_rank": 15,
"to_rank": 12,
"delta_places": 3,
"pnl": "420.25",
"timestamp": 1737331200000
}
Mise à jour de l'écart en compétition (authentifié)
Distance jusqu'au rang situé juste au-dessus de vous.
{
"type": "CompetitionGapUpdate",
"wallet_address": "0x1234...abcd",
"competition_id": 7,
"rank": 12,
"next_rank": 11,
"gap_metric_value": "50.00",
"timestamp": 1737331200000
}
Classement final de compétition (authentifié)
Envoyé lorsqu'une compétition se termine, avec vos résultats finaux.
{
"type": "CompetitionFinalStanding",
"wallet_address": "0x1234...abcd",
"competition_id": 7,
"rank": 12,
"pnl": "420.25",
"volume": "25000",
"efficiency": "0.01681",
"medal": null,
"timestamp": 1737331200000
}
Cotations RFQ (authentifié)
Cotations reçues en réponse à la soumission de votre 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 du statut RFQ (authentifié)
Changement de statut d'un RFQ que vous avez soumis.
{
"type": "RfqStatusUpdate",
"rfq_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "executed",
"taker_wallet": "0x1234...abcd"
}
Erreur
Message d'erreur du serveur.
{
"type": "Error",
"message": "Invalid channel: foobar"
}
Authentification
Les canaux authentifiés nécessitent un message d'identification du portefeuille après la connexion :
{"type": "Authenticate", "wallet": "0x1234567890abcdef..."}
Les messages sur les 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`);
}
};