# Python WebSocket Guide for Backpack Exchange API

This guide demonstrates how to use the Backpack Exchange WebSocket API with Python. WebSockets provide real-time data streams for market data and account updates.

### Prerequisites <a href="#prerequisites" id="prerequisites"></a>

Get your API keys if you are going to use private streams: <https://backpack.exchange/portfolio/settings/api-keys>

Install the required Python libraries:

* websockets - for WebSocket connections
* cryptography - for X-Signature (private streams only)

```
pip install websockets cryptography
```

Install dotenv-python to securely manage your keys using environment variables if you are going to use private streams

```
pip install python-dotenv
```

Create a .env file and store your keys like this:

```
PUBLIC_KEY=zDIJj9qneWIY0IYZ5aXoHcNMCm+XDhVcTssiT0HyY0A=
SECRET_KEY=4odxgSUxFrC/zsKWZF4OQwYAgnNu9hnWH3NxWfLAPz4=
```

Create a .gitignore file and add .env to exclude it from version control.

```
.env
```

Import the necessary libraries:

```
import json
import asyncio
import websockets
import base64
from time import time
import os
from cryptography.hazmat.primitives.asymmetric import ed25519
from dotenv import load_dotenv, find_dotenv
```

### WebSocket API Basics <a href="#websocket-api-basics" id="websocket-api-basics"></a>

The Backpack Exchange WebSocket API is available at `wss://ws.backpack.exchange`.

WebSocket streams are named using the format: `<type>.<symbol>`

For example:

* `depth.SOL_USDC` - Order book for SOL/USDC
* `trade.SOL_USDC` - Trades for SOL/USDC

### Why Use Async with WebSockets <a href="#why-use-async-with-websockets" id="why-use-async-with-websockets"></a>

WebSockets are designed for long-lived connections that receive data in real-time. Using asynchronous programming with WebSockets offers several advantages:

1. **Non-blocking I/O**: Async allows your application to handle multiple connections without blocking the main thread.
2. **Resource Efficiency**: Async uses fewer resources than creating multiple threads for concurrent connections.
3. **Better Performance**: Async can handle many connections with less overhead than synchronous approaches.
4. **Real-time Processing**: Async is ideal for real-time data streams where you need to continuously receive and process data.

Synchronous approaches have several drawbacks:

1. **Complex Threading**: Requires manual thread management
2. **Resource Intensive**: Each connection needs its own thread
3. **Difficult Error Handling**: Error propagation across threads is complex
4. **Scaling Issues**: Does not scale well with many connections

The async approach (as shown in our examples) is much cleaner, more efficient, and easier to maintain.

### Public Streams <a href="#public-streams" id="public-streams"></a>

Public streams don't require authentication. You can subscribe to them directly.

#### Example: Subscribing to a Public Stream <a href="#example-subscribing-to-a-public-stream" id="example-subscribing-to-a-public-stream"></a>

```
async def subscribe_to_public_stream():
    uri = "wss://ws.backpack.exchange"

    async with websockets.connect(uri) as websocket:
        # Subscribe to the depth stream for SOL/USDC
        subscribe_message = {
            "method": "SUBSCRIBE",
            "params": ["depth.SOL_USDC"]
        }

        await websocket.send(json.dumps(subscribe_message))
        print(f"Subscribed to depth.SOL_USDC stream")

        # Process incoming messages
        while True:
            response = await websocket.recv()
            data = json.loads(response)
            print(f"Received: {data}")

            # You can process the data here based on your needs
            # For example, update a local order book

# To run the async function in a Jupyter notebook, use:
# await subscribe_to_public_stream()
#
# TO run in your code
# asyncio.run(subscribe_to_public_stream())  
```

#### Example: Subscribing to Multiple Public Streams <a href="#example-subscribing-to-multiple-public-streams" id="example-subscribing-to-multiple-public-streams"></a>

```
async def subscribe_to_multiple_streams():
    uri = "wss://ws.backpack.exchange"

    async with websockets.connect(uri) as websocket:
        # Subscribe to multiple streams
        subscribe_message = {
            "method": "SUBSCRIBE",
            "params": ["depth.SOL_USDC", "trade.SOL_USDC"]
        }

        await websocket.send(json.dumps(subscribe_message))
        print(f"Subscribed to multiple streams")

        # Process incoming messages
        while True:
            response = await websocket.recv()
            data = json.loads(response)
            print(f"Received: {data}")

            # Process different stream data based on the stream name
            if "stream" in data and "data" in data:
                stream_name = data["stream"]
                stream_data = data["data"]

                if stream_name.startswith("depth."):
                    # Process order book data
                    print(f"Order book update: {stream_data}")
                elif stream_name.startswith("trade."):
                    # Process trade data
                    print(f"Trade update: {stream_data}")

# Run the async function in a Jupyter notebook:
# await subscribe_to_multiple_streams()
#
# To run in your code
# asyncio.run(subscribe_to_multiple_streams())
```

### Private Streams <a href="#private-streams" id="private-streams"></a>

Private streams require authentication with your API keys. These streams are prefixed with `account.` and provide updates about your account.

#### Authentication for Private Streams <a href="#authentication-for-private-streams" id="authentication-for-private-streams"></a>

To authenticate for private streams, you need to:

1. Create a signature string of the form: `instruction=subscribe&timestamp=1614550000000&window=5000`
2. Sign it with your private key
3. Include the signature data in your subscription message as an array: `"signature": ["<verifying key>", "<signature>", "<timestamp>", "<window>"]`

Private streams are prefixed with `account.` and require signature data to be submitted in the subscribe parameters. The verifying key and signature should be base64 encoded.

```
# Load API keys from .env file
# load_dotenv(find_dotenv())
# public_key = os.getenv("PUBLIC_KEY")
# secret_key = os.getenv("SECRET_KEY")

# For demonstration purposes only - don't hardcode keys in production
public_key = "5+yQgwU0ZdJ/9s+GXfuPFfo7yQQpl9CgvQedJXne30o="
secret_key = "TDSkv44jf/iD/QCKkyCdixO+p1sfLXxk+PZH7mW/ams="

# Create private key from secret key
private_key = ed25519.Ed25519PrivateKey.from_private_bytes(
    base64.b64decode(secret_key)
)
```

#### Example: Subscribing to a Private Stream <a href="#example-subscribing-to-a-private-stream" id="example-subscribing-to-a-private-stream"></a>

```
async def subscribe_to_private_stream():
    uri = "wss://ws.backpack.exchange"

    # Generate authentication parameters
    timestamp = int(time() * 1e3)  # Unix time in milliseconds
    window = "5000"  # Time window in milliseconds

    # Create signature string
    sign_str = f"instruction=subscribe&timestamp={timestamp}&window={window}"

    # Sign the string
    signature_bytes = private_key.sign(sign_str.encode())
    encoded_signature = base64.b64encode(signature_bytes).decode()

    async with websockets.connect(uri) as websocket:
        # Subscribe to the order stream with authentication
        subscribe_message = {
            "method": "SUBSCRIBE",
            "params": ["account.orderUpdate"],
            "signature": [public_key, encoded_signature, str(timestamp), window]
        }

        await websocket.send(json.dumps(subscribe_message))
        print(f"Subscribed to account.order stream")

        # Process incoming messages
        while True:
            response = await websocket.recv()
            data = json.loads(response)
            print(f"Received: {data}")

            # Process order updates
            if "stream" in data and data["stream"] == "account.order" and "data" in data:
                order_data = data["data"]
                print(f"Order update: {order_data}")

# Run the async function in a Jupyter notebook:
await subscribe_to_private_stream()
# 
# If using nest_asyncio (recommended):
# asyncio.run(subscribe_to_private_stream())
```

### WebSocket Ping/Pong <a href="#websocket-ping-pong" id="websocket-ping-pong"></a>

WebSocket connections require a ping-pong mechanism to keep the connection alive. The good news is that the Python `websockets` library handles this automatically

### Sources <a href="#sources" id="sources"></a>

For more information visit the official documentation: <https://docs.backpack.exchange/#tag/Streams>
