Metadata-Version: 2.4
Name: quantumsignals-client
Version: 0.6.0
Summary: Python client for Quantum Signals APIs (streaming, inference, key management, backtest)
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.12
Requires-Dist: httpx-sse>=0.4.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: pydantic-settings>=2.10.1
Requires-Dist: pydantic>=2.11.5
Requires-Dist: structlog>=24.1.0
Description-Content-Type: text/markdown

# Quantum Signals Client

Python client library for Quantum Signals APIs.

## Overview

This package provides a Python client for interacting with Quantum Signals services:

- **Streaming**: SSE-based signal streaming
- **Inference**: Model catalog and prediction endpoints
- **Key Management**: API key CRUD operations
- **Backtest**: Backtest results and metadata

## Configuration

The client reads configuration from environment variables:

```bash
# Required:
export QUANTUMSIGNALS_API_KEY=foobar
# Optional, defaults to production URL:
export QUANTUMSIGNALS_BASE_URL=http://localhost:8000
```

The API key can also be set explicitly:
```python
from quantumsignals.client import Client
client = Client(api_key="foobar")
client.set_api_key("foobar")
```

## Usage

### Init

```python
from quantumsignals.client import Client
client = Client()
```

### Fetching Available Models

```python
models = client.get_model_catalog()
```

### Streaming Signals

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

#### Filtering by Symbols and Models

```python
# Stream only specific symbols
for signal in client.stream_signals(symbols=["AAPL", "MSFT"]):
    print(signal)

# Stream only specific models
for signal in client.stream_signals(models=["model_v1", "model_v2"]):
    print(signal)

# Combine filters (AND logic)
for signal in client.stream_signals(
    symbols=["AAPL", "MSFT"],
    models=["model_v1"]
):
    print(signal)
```

#### Replay from Start of Trading Day

Use `replay_day_from_start=True` to replay all signals from the beginning of the
trading day (9:30 AM ET) as fast as possible, then continue with live streaming.
This is useful when connecting mid-day to catch up on all signals that were
generated earlier.

```python
# Replay all signals from market open, then continue live
for signal in client.stream_signals(replay_day_from_start=True):
    print(f"{signal.signal_time_utc} | {signal.symbol}: {signal.signal}")

# Combine with filters
for signal in client.stream_signals(
    symbols=["AAPL"],
    models=["model_v1"],
    replay_day_from_start=True
):
    print(signal)
```

The server sends a `replay_status` event when replay completes and transitions
to live streaming. The client handles this automatically - you'll simply see
historical signals arrive rapidly, followed by live signals at their normal pace.

#### Handling Null Signals

When the market is closed but data is still arriving, you'll receive "null signals"
with `signal=None` and a non-zero `code`. These provide visibility into model
activity even when not actively trading.

```python
from quantumsignals.client.models import SignalCode

for signal in client.stream_signals():
    if signal.is_null_signal():
        # Market is closed, no prediction made
        print(f"Null signal: {signal.code_message}")
        continue

    # Process normal trading signal
    print(f"{signal.symbol}: {signal.signal}")
```

Signal codes:
- `0` (NORMAL): Normal trading signal with valid prediction
- `1` (MARKET_CLOSED): Outside trading hours - no prediction made
- `2` (STALE_CACHE): Inference cache stale for this model's market - predictor refused to serve
- `3` (MODEL_TRADING_END): Model has finished its trading day - past per-model trading_end_time, before market close

Older client versions that pre-date a new code value still deserialize successfully (`signal.code` is a plain integer). `is_null_signal()` returns `True` for any non-`NORMAL` code, and `signal.code_message` falls back to `"Unknown code: N"` so unrecognised codes degrade gracefully — upgrade the client at your leisure to get the human-readable description.

#### Automatic Reconnection

The client automatically reconnects with exponential backoff if the connection
is lost. On reconnection, it sends a `Last-Event-ID` header to recover any
missed events from the server's buffer (last ~100 events).

```python
# Reconnection is enabled by default
for signal in client.stream_signals(auto_reconnect=True):  # default
    print(signal)

# Disable if you want to handle reconnection yourself
for signal in client.stream_signals(auto_reconnect=False):
    print(signal)
```

### Backtest Operations

```python
# Get backtest metadata
metadata = client.get_backtest_metadata(
    model_family="Pythia",
    symbol="AAPL"
)

# Get historical predictions (JSON, CSV, or Parquet)
results = client.get_historical_predictions(
    model_family="Pythia-aB24X",
    symbol="NVDA",
    format="json",
    page=1,
    page_size=100
)

# Get daily backtest summaries
daily = client.get_daily_backtests(
    model_family="Pythia-aB24X",
    symbol="NVDA",
    format="json"
)
```

### Context Manager Usage

```python
# Properly close HTTP connections
with Client() as client:
    models = client.get_model_catalog()
    # Client automatically closes on exit
```

## Development

This is a workspace package in the QS1 monorepo. See the main repository README for development setup.
