API Documentation

QuantumSignals API Documentation

Welcome to the QuantumSignals API documentation. This documentation covers all public APIs for the QuantumSignals trading signal platform.

Getting Started

New to QuantumSignals? Start here:
Python Client
Official Python client library for the easiest integration.
Python Client Guide
Quickstart Guide
Get up and running in 5 minutes with our quickstart guide.
Get started
Authentication
Learn how to authenticate with the API using API keys.
Authentication guide
API Reference
Complete API endpoint documentation with examples.
API Reference

Services

QuantumSignals is composed of four microservices, each providing specific functionality:
Streaming Service
Real-time trading signal delivery via Server-Sent Events (SSE). Signals are continuously broadcast as they become available, allowing clients to maintain a persistent connection and receive updates in real-time.

Key Features: - Real-time signal delivery using SSE - Automatic reconnection support - Low latency signal propagation - Authentication via API key
API Reference
Inference Service
Model catalog and prediction serving. Browse available models and their performance metrics.

Key Features: - Model catalog with metadata - Version management - Performance metrics
API Reference
Key Server
API key management and authentication. Create, list, and delete API keys for accessing protected endpoints.

Key Features: - OIDC-based user authentication - API key CRUD operations - TTL support for temporary keys
API Reference
Backtest Service
Historical strategy backtesting results. Query and download backtest results in multiple formats.

Key Features: - Backtest metadata retrieval - Results in JSON, CSV, or Parquet - Accuracy metrics and analysis
API Reference

API Versions

Version
v1
Status
Current
Description
Stable production API

Need Help?

What's Next?

  1. Quickstart Guide - Get your API key and make your first request
  2. Authentication - Understand authentication and security
  3. Complete API Reference - Explore all endpoints in detail
On this page

Executive Summary

Quickstart Guide

Get started with the QuantumSignals API in 5 minutes using the official Python client.

Prerequisites

  • Python 3.10+
  • Access to the QS1 workspace for client installation

Installation

First, install the SignalQ Python client:
uv sync --package signalq_client
  

Step 1: Authenticate

The SignalQ client uses API key authentication. Obtain your API key from your administrator or the QuantumSignals dashboard:
from signalq.client import Client

# Create client with your API key
client = Client(api_key="your-api-key")

print("Client ready!")
  
For production use, load the API key from environment variables:
import os
from signalq.client import Client

client = Client(api_key=os.getenv("SIGNALQ_API_KEY"))
  

Step 2: Fetch Available Models

Get the catalog of available trading models:
# Get model catalog
models = client.get_model_catalog()

# Display available models
for model_id, model in models.items():
    print(f"Model: {model_id}")
    print(f"  Symbols: {', '.join(model.trained_on_symbols)}")
    print(f"  Training period: {model.trained_date_range[0]} to {model.trained_date_range[1]}")
  

Step 3: Stream Trading Signals

Stream real-time trading signals using a simple iterator pattern:
# Stream signals (runs indefinitely)
for signal in client.stream_signals():
    print(f"{signal.symbol}: {signal.signal}")  # 0 (Down), 1 (Stable), 2 (Up)

    # Process signal
    if signal.signal == 2:
        print(f"  → UP / BUY signal for {signal.symbol}")
    elif signal.signal == 0:
        print(f"  → DOWN / SELL signal for {signal.symbol}")
    elif signal.signal == 1:
        print(f"  → STABLE / HOLD signal for {signal.symbol}")
The client handles all the complexity of Server-Sent Events (SSE) parsing and connection management.

Step 4: Fetch Backtest Results

Access historical backtest results for model evaluation:
# First, get the model catalog to find the model hash
models = client.get_model_catalog()

# Select a model and get its hash (model_hash is required, not model_id)
model_id = list(models.keys())[0]  # Get first available model
model = models[model_id]
model_hash = model.hash
symbol = model.trained_on_symbols[0]

# Get most recent backtest for this model/symbol
backtest = client.get_most_recent_backtest(
    model_hash=model_hash,
    symbol=symbol
)

backtest_id = backtest.id
print(f"Latest backtest ID: {backtest_id}")

# Get detailed results
results = client.get_backtest_results(
    backtest_id=backtest_id,
    format="json",
    page=1,
    page_size=100
)

# Display results
for row in results['data']:
    print(f"Time: {row['date_time']}, Prediction: {row['prediction']}, PnL: {row['cumulative_pnl']:.2f}")

# Get accuracy metrics
accuracy = client.get_backtest_accuracy(backtest_id=backtest_id, format="json")
print(f"Model accuracy: {accuracy}")
  

Complete Example

Here's a complete, production-ready example:
import os
from signalq.client import Client, APIError

# Get API key from environment
api_key = os.getenv("SIGNALQ_API_KEY")

# For development, use interactive login
# client = Client()
# client.login()

# For production, use API key from environment
client = Client(api_key=api_key)

try:
    # 1. Get available models
    print("Available models:")
    models = client.get_model_catalog()
    for model_id, model in models.items():
        print(f"  - {model_id}: {', '.join(model.trained_on_symbols)}")

    # 2. Stream and process signals
    print("\nStreaming signals (Ctrl+C to stop)...")
    for signal in client.stream_signals():
        print(f"{signal.time} | {signal.symbol}: {signal.signal}")

        # Execute trading logic based on signal
        if signal.signal == 1:
            print(f"  → Executing BUY for {signal.symbol}")
            # execute_buy(signal.symbol)
        elif signal.signal == -1:
            print(f"  → Executing SELL for {signal.symbol}")
            # execute_sell(signal.symbol)

except APIError as e:
    print(f"API error: {e}")
except KeyboardInterrupt:
    print("\nStopped by user")
finally:
    client.close()
  
Best Practice: Use a context manager to ensure proper cleanup:
import os
from signalq.client import Client

with Client(api_key=os.getenv("SIGNALQ_API_KEY")) as client:
    models = client.get_model_catalog()
    print(f"Found {len(models)} models")

    for signal in client.stream_signals():
        process_signal(signal)
# Client automatically closes here
  

Next Steps

Advanced: Direct HTTP Integration

For users who cannot use the Python client (e.g., integrating from other languages or custom tooling), here are examples using raw HTTP calls with httpx.
Getting Models (httpx)
import httpx

API_KEY = "your-api-key-here"
BASE_URL = "https://api.quantumsignals.com"

async def get_models():
    async with httpx.AsyncClient() as client:
        response = await client.get(
            f"{BASE_URL}/v1/models",
            headers={"x-api-key": API_KEY},
        )
        response.raise_for_status()
        return response.json()

models = await get_models()
  
Streaming Signals (httpx)
import httpx
import json

async def stream_signals():
    async with httpx.AsyncClient() as client:
        async with client.stream(
            "GET",
            f"{BASE_URL}/v1/signals",
            headers={"x-api-key": API_KEY},
            timeout=None,  # No timeout for streaming
        ) as response:
            response.raise_for_status()

            async for line in response.aiter_lines():
                if line.startswith("data:"):
                    signal_data = line[5:].strip()
                    signal = json.loads(signal_data)
                    print(f"Signal: {signal}")

await stream_signals()
  
Getting Backtest Results (httpx)
import httpx

async def get_backtest_results(backtest_id: str):
    async with httpx.AsyncClient() as client:
        response = await client.get(
            f"{BASE_URL}/v1/backtest/results",
            params={"backtest_id": backtest_id, "format": "json", "page": 1, "page_size": 100},
            headers={"x-api-key": API_KEY},
        )
        response.raise_for_status()
        return response.json()

results = await get_backtest_results("your-backtest-id")
  
Note: The Python client is the recommended approach for Python users as it handles authentication, connection management, error handling, and SSE parsing automatically.

Need Help?

On this page

Executive Summary

Authentication

QuantumSignals API uses API key authentication for most endpoints. API keys are managed through the Key Server and are passed via the x-api-key HTTP header.

Getting Started with Authentication

Using the Python Client (Recommended)
The easiest way to authenticate is using the official SignalQ Python client:
from signalq.client import Client

# Use your API key
client = Client(api_key="qsk_your_api_key_here")
  
For production environments, use environment variables:
import os
from signalq.client import Client

api_key = os.getenv("SIGNALQ_API_KEY")
if not api_key:
    raise ValueError("SIGNALQ_API_KEY environment variable required")

client = Client(api_key=api_key)
  
Obtain your API key from your administrator or the QuantumSignals dashboard.

Manual Authentication Flow

For non-Python integrations or custom implementations:
Step 1: Authenticate with OIDC
The Key Server uses OIDC (OpenID Connect) for user authentication. You'll need to obtain an access token from your identity provider (Zitadel).
Step 2: Create an API Key
Once authenticated, create an API key using the Key Server:
POST /v1/keys
Authorization: Bearer YOUR_OIDC_TOKEN
Content-Type: application/json

{
  "name": "My Trading Bot Key",
  "ttl": 86400  # Optional: TTL in seconds (default: never expires)
}
  
Response:
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "key": "qsk_1234567890abcdef",
  "name": "My Trading Bot Key",
  "created_at": "2025-01-15T10:30:00Z",
  "expires_at": null
}
Important: Save the key value securely - it will only be shown once!

Using Your API Key

Include your API key in the x-api-key header for all authenticated requests:
GET /v1/signals
x-api-key: qsk_1234567890abcdef
  

Which Endpoints Require Authentication?

Authenticated Endpoints (require x-api-key)
  • GET /v1/signals - Streaming signals
  • GET /v1/models - Model catalog
  • GET /v1/backtest - Backtest metadata
  • GET /v1/backtest/results - Backtest results
  • GET /v1/backtest/accuracy - Backtest accuracy metrics
  • GET /v1/most_recent_backtest - Most recent backtest
Key Management Endpoints (require OIDC token)
These endpoints use OIDC token authentication (via Authorization: Bearer header) for managing API keys:
  • POST /v1/keys - Create API key
  • GET /v1/keys - List your API keys
  • DELETE /v1/keys - Delete an API key
Note: These endpoints are typically accessed by administrators via the REST API directly, not through the Python client.

Managing API Keys (REST API)

List Your Keys
GET /v1/keys
Authorization: Bearer YOUR_OIDC_TOKEN
  
Delete a Key
DELETE /v1/keys?key_id=550e8400-e29b-41d4-a716-446655440000
Authorization: Bearer YOUR_OIDC_TOKEN
  

Security Best Practices

  • Never commit API keys to version control
  • Rotate keys regularly - delete old keys and create new ones
  • Use environment variables to store keys in your applications
  • Use different keys for different environments (dev, staging, production)
  • Set TTL for keys used in temporary scripts or testing
  • Monitor key usage through the Key Server

Troubleshooting

401 Unauthorized
  • Check that you're including the x-api-key header
  • Verify the key is correct (no typos, whitespace)
  • Confirm the key hasn't been deleted
403 Forbidden
  • The key exists but doesn't have permission for this endpoint
  • Contact support to upgrade your access level
429 Too Many Requests
  • You've exceeded the rate limit
  • Consider implementing exponential backoff
On this page

Executive Summary

QuantumSignals Python Client

The official Python client library for the QuantumSignals API. This client provides a simple, intuitive interface for authentication, streaming signals, managing API keys, and accessing backtest results.

Installation

The SignalQ client is part of the QS1 workspace. Install with:
uv sync --package signalq_client
  

Quick Start

Get started in 30 seconds:
from signalq.client import Client

# Create client and authenticate (opens browser for OIDC login)
client = Client()
client.login()

# Stream real-time trading signals
for signal in client.stream_signals():
    print(f"{signal.time} | {signal.symbol}: {signal.signal}")
  

Authentication

The SignalQ client uses API key authentication. Obtain your API key from your administrator or the QuantumSignals dashboard.
from signalq.client import Client

# Pass API key directly
client = Client(api_key="qsk_your_api_key_here")

# Client is ready to use
models = client.get_model_catalog()
  
For production use, load the API key from environment variables:
import os
from signalq.client import Client

api_key = os.getenv("SIGNALQ_API_KEY")
if not api_key:
    raise ValueError("SIGNALQ_API_KEY environment variable required")

client = Client(api_key=api_key)
  

Core Features

Model Catalog
Get information about available trading models:
# Get catalog as dict[model_id -> ModelDescription]
models = client.get_model_catalog()

for model_id, model in models.items():
    print(f"Model: {model_id}")
    print(f"  Trained on: {', '.join(model.trained_on_symbols)}")
    print(f"  Date range: {model.trained_date_range[0]} to {model.trained_date_range[1]}")
    print(f"  Hash: {model.hash}")
  
Signal Streaming
Stream real-time trading signals via Server-Sent Events:
# Stream indefinitely
for signal in client.stream_signals():
    print(f"Time: {signal.time}")
    print(f"Symbol: {signal.symbol}")
    print(f"Signal: {signal.signal}")  # 0 (down), 1 (stable), 2 (up)
    print(f"Model: {signal.model}")

    # Process signal
    if signal.signal == 1:
        execute_buy(signal.symbol)
    elif signal.signal == 0:
        execute_sell(signal.symbol)
  
Signal Data Model: - time: int - Unix timestamp - symbol: str - Trading symbol (e.g., "AAPL") - signal: int - Signal value: -1 (sell), 0 (hold), 1 (buy) - model: str - Model identifier
Backtest Operations
Access historical backtest results for model evaluation:

Get Most Recent Backtest
# First, get the model catalog to find the model hash
models = client.get_model_catalog()

# Select a model and get its hash (model_hash is required, not model_id)
model_id = "Pythia-aB24X"  # or list(models.keys())[0] for first model
model = models[model_id]
model_hash = model.hash
symbol = model.trained_on_symbols[0]  # e.g., "ES"

# Get the most recent backtest for this model/symbol combination
backtest = client.get_most_recent_backtest(
    model_hash=model_hash,
    symbol=symbol
)

print(f"Backtest ID: {backtest.id}")
print(f"Symbol: {backtest.symbol}")
  
Get Backtest Metadata
from signalq.client import BacktestMetadata

metadata: BacktestMetadata = client.get_backtest_metadata(
    backtest_id="550e8400-e29b-41d4-a716-446655440000"
)

print(f"Model: {metadata.model_id}")
print(f"Symbol: {metadata.symbol}")
print(f"Date range: {metadata.backtest_from_date} to {metadata.backtest_to_date}")
print(f"Status: {metadata.status}")
  
Get Backtest Results
# Get results in JSON format (paginated)
results = client.get_backtest_results(
    backtest_id="550e8400-e29b-41d4-a716-446655440000",
    format="json",
    page=1,
    page_size=100
)

for row in results['data']:
    print(f"{row['time']}: Position={row['position']}, PnL={row['pnl']}")

# Get results in CSV format -- this returns bytes which must be written
# to a file
csv_data = client.get_backtest_results(
    backtest_id="550e8400-e29b-41d4-a716-446655440000",
    format="csv"
)
with open("test.csv", "wb") as f:
    f.write(csv_data)

# Get results in Parquet format; this returns bytes which must be written
# to a file
parquet_data = client.get_backtest_results(
    backtest_id="550e8400-e29b-41d4-a716-446655440000",
    format="parquet"
)
with open("test.parquet", "wb") as f:
    f.write(parquet_data)
  
Result Data Model: - time: int - Unix timestamp - signal: int - Signal at that time - position: int - Portfolio position - pnl: float - Profit/loss for the period - cumulative_pnl: float - Cumulative profit/loss
Get Backtest Accuracy Metrics
# Get accuracy metrics in JSON
accuracy = client.get_backtest_accuracy(
    backtest_id="550e8400-e29b-41d4-a716-446655440000",
    format="json"
)

print(f"Accuracy: {accuracy['accuracy']:.2%}")
print(f"Precision: {accuracy['precision']:.2%}")
print(f"Recall: {accuracy['recall']:.2%}")

# Filter by date range
accuracy = client.get_backtest_accuracy(
    backtest_id="550e8400-e29b-41d4-a716-446655440000",
    format="json", # this returns a dict
    from_date="2024-01-01", # optional
    to_date="2024-12-31" # optional
)
  

Best Practices

Use Context Managers
Properly close HTTP connections using context managers:
import os
from signalq.client import Client

with Client(api_key=os.getenv("SIGNALQ_API_KEY")) as client:
    models = client.get_model_catalog()
    # Client automatically closes on exit
  
Handle Exceptions
The client provides specific exceptions for error handling:
import os
from signalq.client import Client, APIKeyError, APIError

client = Client(api_key=os.getenv("SIGNALQ_API_KEY"))

try:
    models = client.get_model_catalog()
except APIKeyError as e:
    print(f"API key issue: {e}")
except APIError as e:
    print(f"API request failed: {e}")
    print(f"Status code: {e.status_code}")
    print(f"Response: {e.response_body}")
  
Production Signal Streaming
For production signal streaming with error recovery:
from signalq.client import Client, APIError
import time
import os

def stream_signals_with_retry(max_retries=3):
    """Stream signals with automatic retry on failure."""
    client = Client(api_key=os.getenv("SIGNALQ_API_KEY"))
    retry_count = 0

    while retry_count < max_retries:
        try:
            for signal in client.stream_signals():
                # Process signal
                process_signal(signal)
                retry_count = 0  # Reset on successful signal

        except APIError as e:
            retry_count += 1
            wait_time = 2 ** retry_count  # Exponential backoff
            print(f"Stream error: {e}. Retrying in {wait_time}s...")
            time.sleep(wait_time)
        except KeyboardInterrupt:
            print("Stopping stream...")
            break
        finally:
            client.close()
  

Complete Example

Here's a complete production-ready example:
import os
from signalq.client import Client, APIError
import logging

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def main():
    """Main trading bot using SignalQ client."""

    # Initialize client with API key from environment
    api_key = os.getenv("SIGNALQ_API_KEY")
    if not api_key:
        raise ValueError("SIGNALQ_API_KEY environment variable required")

    with Client(api_key=api_key) as client:
        # Get available models
        logger.info("Fetching model catalog...")
        models = client.get_model_catalog()
        logger.info(f"Available models: {list(models.keys())}")

        # Stream and process signals
        logger.info("Starting signal stream...")
        try:
            for signal in client.stream_signals():
                logger.info(
                    f"Signal received: {signal.symbol} -> {signal.signal} "
                    f"(model: {signal.model}, time: {signal.time})"
                )

                # Execute trading logic
                if signal.signal == 2:
                    logger.info(f"BUY signal for {signal.symbol}")
                    # execute_buy(signal.symbol)
                elif signal.signal == 0:
                    logger.info(f"SELL signal for {signal.symbol}")
                    # execute_sell(signal.symbol)

        except APIError as e:
            logger.error(f"API error: {e}")
            raise
        except KeyboardInterrupt:
            logger.info("Shutting down...")

if __name__ == "__main__":
    main()
  

Troubleshooting

Connection Errors During Streaming
Signal streaming is a long-lived connection. If you encounter disconnections: - Implement retry logic with exponential backoff - Check your network stability - Verify the streaming service is running

API Reference

For detailed REST API documentation and advanced HTTP integration, see: - REST API Quickstart - Streaming Service Documentation - Authentication Guide
On this page

Executive Summary