WebSocket API
Echtzeit-Datenstreaming für den Hypercall-Optionshandel.
Sehen Sie sich die Interaktive WebSocket-API-Referenz für ein besseres Nutzungserlebnis mit Live-Beispielen und Schema-Details an.
Laden Sie die AsyncAPI-Spezifikation für die programmatische Nutzung herunter.
Verbindung
Verbinden Sie sich mit wss://HOST/ws:
Endpunkte:
- Produktion:
wss://api.hypercall.xyz/ws - Lokal:
ws://localhost:3000/ws
Das Testnet ist vorübergehend deaktiviert, bis Hypercall mehr Testnet-HYPE erwirbt.
Wallet-Identifikation
Um Daten auf authentifizierten Kanälen (Orders, Fills, Portfolio) zu empfangen, identifizieren Sie Ihr Wallet nach dem Verbinden, indem Sie eine Authenticate-Nachricht senden:
{"type": "Authenticate", "wallet": "0x1234..."}
Der Server antwortet mit einer Bestätigung:
{"type": "Authenticated", "wallet": "0x1234..."}
Nach dem Empfang von Authenticated können Sie authentifizierte Kanäle abonnieren. Wenn die Wallet-Adresse ungültig ist, antwortet der Server mit einer Error-Nachricht und die Verbindung bleibt geöffnet.
Der ?wallet=-Query-Parameter wird aus Gründen der Abwärtskompatibilität weiterhin unterstützt, ist jedoch veraltet und wird in einer zukünftigen Version entfernt. Bevorzugen Sie den oben beschriebenen nachrichtenbasierten Ansatz.
Verbindungsaktivität
Der Server erzwingt einen WebSocket-Heartbeat:
- Sendet alle 20 Sekunden einen
Ping-Steuerframe - Erwartet einen passenden
Ponginnerhalb von 60 Sekunden - Schließt die Verbindung mit dem Schließcode
1008und dem Grundpong timeout, wenn der Client nicht mehr antwortet
Browser-WebSocket-Implementierungen verarbeiten Ping/Pong automatisch. Viele Rust-WebSocket-Bibliotheken, darunter tungstenite und tokio-tungstenite, verarbeiten das Steuerframe-Ping/Pong ebenfalls für Sie. Prüfen Sie die Dokumentation der Bibliothek Ihres Clients, bevor Sie eine manuelle Pong-Behandlung hinzufügen. Benutzerdefinierte oder Raw-Socket-Implementierungen müssen auf Ping-Frames mit Pong antworten.
Slow-Consumer-Recovery
Der Server schließt eine /ws-Verbindung, die ausgehende Daten nicht innerhalb der konfigurierten Sicherheitsobergrenze für Nachrichten, kodierte Bytes, Warteschlangenalter oder Socket-Schreibvorgänge abbauen kann. Wenn die Verbindung noch einen Schließframe akzeptieren kann, verwendet der Server den Code 1008 und einen kompakten JSON-Grund:
{"error":"slow_consumer","class":"ordered_public","cause":"message_age","recovery":"snapshot_resubscribe"}
Die Grund-Felder sind:
| Feld | Bedeutung |
|---|---|
class | Zustellungsklasse, deren Frame die Sicherheitsgrenze überschritten hat. |
cause | message_limit, byte_limit, message_age oder write_timeout. |
recovery | Erforderliche nächste Aktion, wie etwa resubscribe, snapshot_resubscribe, portfolio_refetch oder rest_reconcile. |
Nach jeder Trennung: verbinden Sie sich erneut, identifizieren Sie bei Bedarf das Wallet erneut, abonnieren Sie erneut und gleichen Sie den aktuellen Zustand ab, bevor Sie neue Ereignisse verarbeiten. Geordnete öffentliche Kanäle erfordern einen frischen Snapshot. Private Ereigniskanäle erfordern einen Abgleich über die maßgebliche REST-Schnittstelle, da ein Cursor-Replay noch nicht verfügbar ist. Eine vollständig blockierte Verbindung kann beendet werden, bevor sie den Schließgrund lesen kann, daher müssen Clients diesen Recovery-Ablauf auch bei einem unsauberen Schließen verwenden.
Verwenden Sie separate Verbindungen für öffentliche Marktdaten mit hoher Rate und für authentifizierte Befehle oder private Streams. Zustellungsklassen bestimmen die Metriken und das Recovery-Verhalten, aber Frames auf einer Verbindung teilen sich dennoch einen geordneten Socket-Schreibpfad. Ein blockierter öffentlicher Schreibvorgang kann daher spätere private Frames auf derselben Verbindung verzögern, bis die Schreib-Deadline sie schließt.
Kanäle abonnieren
Senden Sie eine JSON-Nachricht zum Abonnieren:
{"type": "Subscribe", "channel": "orderbook"}
Zum Abbestellen:
{"type": "Unsubscribe", "channel": "orderbook"}
Sie erhalten eine Bestätigung:
{"type": "Subscribed", "channel": "orderbook"}
Symbolfilterung
Die Kanäle order_updates und fills unterstützen einen optionalen symbols-Filter. Wenn er angegeben ist, sendet der Server nur Nachrichten, deren Basiswert einem der angegebenen Symbole entspricht.
{"type": "Subscribe", "channel": "order_updates", "symbols": ["BTC"]}
Sowohl reine Basiswerte ("BTC") als auch vollständige Instrumentennamen ("BTC-20260131-100000-C") werden akzeptiert. Um weitere Symbole hinzuzufügen, senden Sie ein weiteres Subscribe. Um bestimmte Symbole zu entfernen:
{"type": "Unsubscribe", "channel": "order_updates", "symbols": ["BTC"]}
Wenn keine symbols angegeben sind, werden alle Updates für Ihr Wallet weitergeleitet.
Optionsketten-Filterung
Der options_chain-Kanal unterstützt die Filterung nach Basiswert-Symbolen, Verfallsdatum und Optionstyp:
{
"type": "Subscribe",
"channel": "options_chain",
"symbols": ["BTC-20260131-100000-C"],
"expiry": "2026-01-31",
"option_type": "call"
}
| Filter | Werte | Standard |
|---|---|---|
symbols | Array vollständiger Instrumentensymbole (z. B. ["BTC-20260131-100000-C"]) | Alle Instrumente |
expiry | Datumszeichenkette "YYYY-MM-DD" | Alle Verfallstermine |
option_type | "call", "put" oder weglassen für beide | Beide |
Verfügbare Kanäle
| Kanal | Authentifizierung erforderlich | Beschreibung |
|---|---|---|
orderbook | Nein | L2-Orderbuch-Updates für alle Symbole |
trades | Nein | Öffentlicher Trade-Feed |
market_updates | Nein | Änderungen an Marktlistungen (erstellt/gelöscht/verfallen) |
options_chain | Nein | Inkrementelle Optionsketten-Updates (filterbar nach Symbolen, Verfall, Optionstyp) |
index_prices | Nein | Echtzeit-Spot-/Indexpreise für alle Basiswerte |
indicative_market_data | Nein | Quote-Provider-Stream mit Allowlist. Noch nicht allgemein verfügbar |
order_updates | Ja | Änderungen an Ihrem Orderstatus (filterbar nach Symbol) |
fills | Ja | Ihre Trade-Fills (filterbar nach Symbol) |
portfolio | Ja | Ihre Positions- und Guthaben-Updates |
liquidation | Ja | Änderungen an Ihrem Liquidationszustand |
competition | Ja | Ihre Competition-GuV-Zusammenfassung, Rang und finale Statistiken |
competition_engagement | Ja | Rangänderungen, Abstand zum nächsten Rang und Endstände |
rfq | Ja | RFQ-Quotes, Statusaktualisierungen und Fill-Benachrichtigungen |
Nachrichtentypen
Order platzieren (authentifiziert)
Platzieren Sie eine Order über den WebSocket-Befehlspfad.
{
"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..."
}
| Feld | Typ | Beschreibung |
|---|---|---|
wallet | string | Wallet-Adresse, die die Order besitzt |
symbol | string | Optionssymbol |
side | string | "Buy" oder "Sell" |
size | string | Kontraktgröße, exakt übereinstimmend mit dem signierten Wert |
price | string | Limitpreis, exakt übereinstimmend mit dem signierten Wert |
tif | string | Optionale Time-in-Force, Standardwert "gtc" |
route | string | Optionale Route. Verwenden Sie "book_only" für routenbewusste WebSocket-Orders. Eine weggelassene Route bleibt mindestens bis zum 4. Juli 2026 akzeptiert. |
client_id | string | Optionale Client-Order-ID |
nonce | integer | Eindeutige Signier-Nonce |
signature | string | EIP-712-PlaceOrder-Signatur |
WebSocket-PlaceOrder leitet derzeit direkt an das Orderbuch weiter. route="best_execution" und route="rfq_only" werden über WebSocket abgelehnt, da dieser Pfad noch kein RPI/RFQ-Routing ausführt. Verwenden Sie POST /order für best_execution.
Orderbuch-Update
L2-Orderbuch-Snapshot/-Update für ein Symbol.
{
"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
}
| Feld | Typ | Beschreibung |
|---|---|---|
symbol | string | Optionssymbol |
bids | array | Bid-Level als [price, size]-Tupel, size in menschenlesbaren Kontrakten |
asks | array | Ask-Level als [price, size]-Tupel, size in menschenlesbaren Kontrakten |
timestamp | integer | Unix-Zeitstempel (Millisekunden) |
Trade
Öffentliches Trade-Ereignis.
{
"type": "Trade",
"symbol": "BTC-20260131-100000-C",
"price": "0.0523",
"size": "5.0",
"side": "buy",
"timestamp": 1737331200000
}
| Feld | Typ | Beschreibung |
|---|---|---|
symbol | string | Optionssymbol |
price | string | Trade-Preis in USD |
size | string | Trade-Größe in Kontrakten |
side | string | Aggressor-Seite (buy oder sell) |
timestamp | integer | Unix-Zeitstempel (Millisekunden) |
Fill (authentifiziert)
Ihre Trade-Fill-Benachrichtigung.
{
"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
}
| Feld | Typ | Beschreibung |
|---|---|---|
order_id | integer | Ihre Order-ID |
fill_id | integer | Fill-ID |
symbol | string | Options-Symbol |
side | string | Handelsseite (buy oder sell) |
price | string | Ausführungspreis in USD |
size | string | Ausführungsgröße in Kontrakten |
timestamp | integer | Unix-Zeitstempel (Millisekunden) |
wallet_address | string | Ihre Wallet-Adresse |
fee | string | Erhobene Handelsgebühr. Gibt 0 zurück, solange die Gebühren des Launch-Venues deaktiviert sind |
trade_id | integer | Eindeutige Trade-ID |
is_taker | boolean | Ob Sie der Taker waren |
builder_code_address | string? | Builder-Code-Wallet (falls vorhanden) |
builder_code_fee | string? | Builder-Code-Gebühr. Gibt null zurück, solange die Gebühren des Launch-Venues deaktiviert sind |
Portfolio-Update (authentifiziert)
Portfolio-Stream-Update für Positionen, Guthaben, Margin und Griechen.
Beispiel für ein Griechen-Update:
{
"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
}
}
Bei leeren Portfolios verwenden Griechen-Updates:
per_leg: []aggregate: null
Wettbewerbs-GuV-Zusammenfassung (authentifiziert)
Wettbewerbs-Stream-Update für die GuV-Anzeige in Kopf-/Fußzeile.
{
"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
}
Wenn kein aktiver Wettbewerb vorliegt, ist active_competition gleich null.
Order-Update (authentifiziert)
Benachrichtigung über eine Statusänderung der Order.
{
"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"
}
Markt-Update
Änderungen bei Markt-Listings.
Markt erstellt:
{
"type": "MarketUpdate",
"action": "Created",
"symbol": "BTC-20260131-100000-C",
"strike": "100000",
"is_call": true,
"underlying": "BTC",
"expiry": 1738281600,
"timestamp": 1737331200000
}
Markt verfallen:
{
"type": "MarketUpdate",
"action": "Expired",
"symbol": "BTC-20260131-100000-C",
"strike": "100000",
"is_call": true,
"underlying": "BTC",
"expiry": 1738281600,
"timestamp": 1738281600000
}
Position verfallen (authentifiziert)
Benachrichtigung, wenn Ihre Position bei Verfall abgerechnet wird.
{
"type": "PositionExpired",
"wallet_address": "0x1234...abcd",
"symbol": "BTC-20260131-100000-C",
"position_size": "10.0",
"settlement_price": "105000",
"settlement_value": "500.0",
"timestamp": 1738281600000
}
Änderung des Liquidationsstatus (authentifiziert)
Änderung des Liquidationsstatus Ihres Kontos.
{
"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
}
| Status | Beschreibung |
|---|---|
Normal | Konto ist gesund |
Warning | Nähert sich einem Margin Call |
Liquidating | Liquidationsauktion aktiv |
Index-Preis-Update
Gebündelte Spot-/Index-Preise für alle Basiswerte.
{
"type": "IndexPriceUpdate",
"prices": [
{"underlying": "BTC", "price": "97250.50"},
{"underlying": "ETH", "price": "3200.00"},
{"underlying": "HYPE", "price": "28.50"}
],
"timestamp": 1737331200000
}
| Feld | Typ | Beschreibung |
|---|---|---|
prices | array | Array von {underlying, price}-Einträgen für jeden erfassten Basiswert |
prices[].underlying | string | Symbol des Basiswerts (z. B. "BTC", "ETH") |
prices[].price | string | Aktueller Spot-/Index-Preis in USD |
timestamp | integer | Unix-Zeitstempel (Millisekunden) |
Indikative Marktdaten
Auf einer Allowlist basierender Quote-Provider-Stream mit aggregiertem bestem Bid/Ask von registrierten Quote-Providern. Dieser Kanal ist noch nicht allgemein verfügbar. Verwenden Sie REST-Marktdaten sowie die authentifizierten Order-/Fill-/Portfolio-Kanäle, sofern Hypercall das Quote-Provider-Streaming für Ihre Integration nicht aktiviert hat.
{
"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
}
| Feld | Typ | Beschreibung |
|---|---|---|
instrument | string | Options-Symbol |
best_bid | string | Optionaler bester aggregierter Bid-Preis |
best_ask | string | Optionaler bester aggregierter Ask-Preis |
bid_iv | number | Optionale implizite Volatilität des besten Bid |
ask_iv | number | Optionale implizite Volatilität des besten Ask |
indicative_bid_size | string | Optionale gesamte Bid-Größe über alle Provider |
indicative_ask_size | string | Optionale gesamte Ask-Größe über alle Provider |
num_providers | integer | Anzahl der aktiven Quote-Provider |
timestamp | integer | Unix-Zeitstempel (Millisekunden) |
Änderung des Wettbewerbsrangs (authentifiziert)
Benachrichtigung, wenn sich Ihr Rang in einem aktiven Wettbewerb ändert.
{
"type": "CompetitionRankChange",
"wallet_address": "0x1234...abcd",
"competition_id": 7,
"from_rank": 15,
"to_rank": 12,
"delta_places": 3,
"pnl": "420.25",
"timestamp": 1737331200000
}
Wettbewerbs-Abstands-Update (authentifiziert)
Abstand zum nächsthöheren Rang über Ihnen.
{
"type": "CompetitionGapUpdate",
"wallet_address": "0x1234...abcd",
"competition_id": 7,
"rank": 12,
"next_rank": 11,
"gap_metric_value": "50.00",
"timestamp": 1737331200000
}
Endplatzierung im Wettbewerb (authentifiziert)
Wird gesendet, wenn ein Wettbewerb mit Ihren Endergebnissen endet.
{
"type": "CompetitionFinalStanding",
"wallet_address": "0x1234...abcd",
"competition_id": 7,
"rank": 12,
"pnl": "420.25",
"volume": "25000",
"efficiency": "0.01681",
"medal": null,
"timestamp": 1737331200000
}
RFQ-Quotes (authentifiziert)
Quotes, die als Antwort auf Ihre RFQ-Einreichung empfangen wurden.
{
"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"
}
RFQ-Status-Update (authentifiziert)
Statusänderung für eine von Ihnen eingereichte RFQ.
{
"type": "RfqStatusUpdate",
"rfq_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "executed",
"taker_wallet": "0x1234...abcd"
}
Fehler
Server-Fehlermeldung.
{
"type": "Error",
"message": "Invalid channel: foobar"
}
Authentifizierung
Authentifizierte Kanäle erfordern nach dem Verbindungsaufbau eine Nachricht zur Wallet-Identifikation:
{"type": "Authenticate", "wallet": "0x1234567890abcdef..."}
Nachrichten auf authentifizierten Kanälen werden gefiltert, sodass nur Daten für Ihr Wallet angezeigt werden. Für WebSocket-Verbindungen ist keine Signatur erforderlich.
Beispiel: Python-Client
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())
Beispiel: TypeScript-Client
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`);
}
};