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 acknowledgment
  • events_processed: Array of event IDs that were processed
  • timestamp: 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