Hello World
This example demonstrates the minimal code needed to connect to the Trading API and receive market events. You'll learn how to:
- Establish a WebSocket connection to the Trading API
- Receive and parse market events (candles, quotes)
- Acknowledge events with
event_ack - Handle ping/pong heartbeat messages
Connection URL
Connect to the Trading API WebSocket at:
- Backtest mode (Engine):
ws://localhost:8081/ws - Live trading (Proxy):
ws://localhost:8082/ws
Python
import asyncio
import json
import uuid
from datetime import datetime
import websockets
async def main():
uri = "ws://localhost:8082/ws"
async with websockets.connect(uri) as ws:
print("Connected to Trading API")
while True:
message = await ws.recv()
event = json.loads(message)
# Handle ping/pong heartbeat
if event.get("type") == "ping":
await ws.send(json.dumps({"type": "pong"}))
continue
# Process market events
event_type = event.get("type")
print(f"Received event: {event_type}")
if event_type == "candle":
bar = event.get("bar", {})
print(f" {bar.get('symbol')} - Close: {bar.get('close')}")
elif event_type == "quote":
quote = event.get("quote", {})
print(f" {quote.get('symbol')} - Bid: {quote.get('bid')} Ask: {quote.get('ask')}")
# Acknowledge the event
ack = {
"type": "event_ack",
"correlation_id": str(uuid.uuid4()),
"events_processed": [event.get("id", "unknown")],
"timestamp": int(datetime.now().timestamp() * 1000)
}
await ws.send(json.dumps(ack))
if __name__ == "__main__":
asyncio.run(main())
JavaScript
const WebSocket = require('ws');
const { v4: uuidv4 } = require('uuid');
const ws = new WebSocket('ws://localhost:8082/ws');
ws.onopen = () => {
console.log('Connected to Trading API');
};
ws.onmessage = (msg) => {
const event = JSON.parse(msg.data);
// Handle ping/pong heartbeat
if (event.type === 'ping') {
ws.send(JSON.stringify({ type: 'pong' }));
return;
}
// Process market events
console.log(`Received event: ${event.type}`);
if (event.type === 'candle') {
const bar = event.bar || {};
console.log(` ${bar.symbol} - Close: ${bar.close}`);
} else if (event.type === 'quote') {
const quote = event.quote || {};
console.log(` ${quote.symbol} - Bid: ${quote.bid} Ask: ${quote.ask}`);
}
// Acknowledge the event
const ack = {
type: 'event_ack',
correlation_id: uuidv4(),
events_processed: [event.id || 'unknown'],
timestamp: Date.now()
};
ws.send(JSON.stringify(ack));
};
ws.onerror = (err) => {
console.error('WebSocket error:', err.message);
};
ws.onclose = () => {
console.log('Disconnected from Trading API');
};
Java
import javax.websocket.*;
import java.net.URI;
import java.util.UUID;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
@ClientEndpoint
public class HelloWorld {
private static final Gson gson = new Gson();
@OnOpen
public void onOpen(Session session) {
System.out.println("Connected to Trading API");
}
@OnMessage
public void onMessage(String message, Session session) throws Exception {
JsonObject event = gson.fromJson(message, JsonObject.class);
String type = event.get("type").getAsString();
// Handle ping/pong heartbeat
if ("ping".equals(type)) {
session.getBasicRemote().sendText("{\"type\":\"pong\"}");
return;
}
// Process market events
System.out.println("Received event: " + type);
if ("candle".equals(type)) {
JsonObject bar = event.getAsJsonObject("bar");
System.out.printf(" %s - Close: %s%n",
bar.get("symbol").getAsString(),
bar.get("close").getAsString());
} else if ("quote".equals(type)) {
JsonObject quote = event.getAsJsonObject("quote");
System.out.printf(" %s - Bid: %s Ask: %s%n",
quote.get("symbol").getAsString(),
quote.get("bid").getAsString(),
quote.get("ask").getAsString());
}
// Acknowledge the event
JsonObject ack = new JsonObject();
ack.addProperty("type", "event_ack");
ack.addProperty("correlation_id", UUID.randomUUID().toString());
ack.add("events_processed", gson.toJsonTree(new String[]{
event.has("id") ? event.get("id").getAsString() : "unknown"
}));
ack.addProperty("timestamp", System.currentTimeMillis());
session.getBasicRemote().sendText(gson.toJson(ack));
}
@OnClose
public void onClose() {
System.out.println("Disconnected from Trading API");
}
public static void main(String[] args) throws Exception {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
URI uri = new URI("ws://localhost:8082/ws");
container.connectToServer(HelloWorld.class, uri);
// Keep the connection open
Thread.sleep(Long.MAX_VALUE);
}
}
Rust
use tokio_tungstenite::{connect_async, tungstenite::Message};
use futures_util::{SinkExt, StreamExt};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use uuid::Uuid;
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Serialize)]
struct EventAck {
#[serde(rename = "type")]
msg_type: String,
correlation_id: String,
events_processed: Vec<String>,
timestamp: u64,
}
#[derive(Serialize)]
struct Pong {
#[serde(rename = "type")]
msg_type: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let url = "ws://localhost:8082/ws";
let (ws_stream, _) = connect_async(url).await?;
println!("Connected to Trading API");
let (mut write, mut read) = ws_stream.split();
while let Some(msg) = read.next().await {
let msg = msg?;
if let Message::Text(text) = msg {
let event: Value = serde_json::from_str(&text)?;
let event_type = event["type"].as_str().unwrap_or("unknown");
// Handle ping/pong heartbeat
if event_type == "ping" {
let pong = Pong { msg_type: "pong".to_string() };
write.send(Message::Text(serde_json::to_string(&pong)?)).await?;
continue;
}
// Process market events
println!("Received event: {}", event_type);
match event_type {
"candle" => {
if let Some(bar) = event.get("bar") {
println!(" {} - Close: {}",
bar["symbol"].as_str().unwrap_or(""),
bar["close"].as_str().unwrap_or(""));
}
}
"quote" => {
if let Some(quote) = event.get("quote") {
println!(" {} - Bid: {} Ask: {}",
quote["symbol"].as_str().unwrap_or(""),
quote["bid"].as_str().unwrap_or(""),
quote["ask"].as_str().unwrap_or(""));
}
}
_ => {}
}
// Acknowledge the event
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)?
.as_millis() as u64;
let ack = EventAck {
msg_type: "event_ack".to_string(),
correlation_id: Uuid::new_v4().to_string(),
events_processed: vec![
event["id"].as_str().unwrap_or("unknown").to_string()
],
timestamp,
};
write.send(Message::Text(serde_json::to_string(&ack)?)).await?;
}
}
Ok(())
}
Go
package main
import (
"encoding/json"
"fmt"
"log"
"time"
"github.com/google/uuid"
"github.com/gorilla/websocket"
)
type EventAck struct {
Type string `json:"type"`
CorrelationID string `json:"correlation_id"`
EventsProcessed []string `json:"events_processed"`
Timestamp int64 `json:"timestamp"`
}
type Pong struct {
Type string `json:"type"`
}
func main() {
url := "ws://localhost:8082/ws"
conn, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
log.Fatal("Connection failed:", err)
}
defer conn.Close()
fmt.Println("Connected to Trading API")
for {
_, message, err := conn.ReadMessage()
if err != nil {
log.Println("Read error:", err)
return
}
var event map[string]interface{}
if err := json.Unmarshal(message, &event); err != nil {
log.Println("Parse error:", err)
continue
}
eventType, _ := event["type"].(string)
// Handle ping/pong heartbeat
if eventType == "ping" {
pong := Pong{Type: "pong"}
conn.WriteJSON(pong)
continue
}
// Process market events
fmt.Printf("Received event: %s\n", eventType)
switch eventType {
case "candle":
if bar, ok := event["bar"].(map[string]interface{}); ok {
fmt.Printf(" %s - Close: %s\n", bar["symbol"], bar["close"])
}
case "quote":
if quote, ok := event["quote"].(map[string]interface{}); ok {
fmt.Printf(" %s - Bid: %s Ask: %s\n",
quote["symbol"], quote["bid"], quote["ask"])
}
}
// Acknowledge the event
eventID := "unknown"
if id, ok := event["id"].(string); ok {
eventID = id
}
ack := EventAck{
Type: "event_ack",
CorrelationID: uuid.New().String(),
EventsProcessed: []string{eventID},
Timestamp: time.Now().UnixMilli(),
}
conn.WriteJSON(ack)
}
}
Key Concepts
Event Acknowledgment
In backtest mode, acknowledging events is required - it controls time simulation. The engine waits for your acknowledgment before advancing to the next event.
In live trading mode, acknowledgment is informational only but still recommended for tracking purposes.
The event_ack message requires:
correlation_id: A unique ID for tracking this acknowledgmentevents_processed: Array of event IDs that were processedtimestamp: Unix timestamp in milliseconds
Heartbeat
The server sends ping messages to check connection health. Your client must respond with pong to keep the connection alive. If no response is received, the connection may be closed.
Next Steps
- Placing Orders - Learn how to submit and manage orders
- Event Handling - Advanced patterns for processing events
- WebSocket Messages - Full message reference