Skip to main content

Order Execution Streaming

Subscribe to real-time order updates, execution reports, fills, and cancellations for your trading activity using Python and gRPC.

Service Definition

Service: connamara.ep3.v1beta1.OrderEntryAPI RPC: CreateOrderSubscription Type: Server-side streaming
service OrderEntryAPI {
    rpc CreateOrderSubscription(CreateOrderSubscriptionRequest)
        returns (stream CreateOrderSubscriptionResponse);
}

Request Parameters

CreateOrderSubscriptionRequest

FieldTypeRequiredDescription
symbolslist[str]NoFilter by symbols. Empty list = all symbols.
accountslist[str]NoFilter by trading accounts. Empty list = all accounts for authenticated user.
snapshot_onlyboolNoIf True, receive snapshot of current orders then close stream. If False (default), receive continuous updates.

Example Request

from connamara.ep3.v1beta1 import order_entry_pb2

# Subscribe to all orders for your accounts
request = order_entry_pb2.CreateOrderSubscriptionRequest(
    symbols=[],
    accounts=[],
    snapshot_only=False
)

# Subscribe to specific symbol
request = order_entry_pb2.CreateOrderSubscriptionRequest(
    symbols=["mlb-ari-sf-2025-09-08"],
    accounts=[],
    snapshot_only=False
)

# Get snapshot only (current state)
request = order_entry_pb2.CreateOrderSubscriptionRequest(
    symbols=[],
    accounts=[],
    snapshot_only=True
)

Response Messages

The stream returns CreateOrderSubscriptionResponse messages with multiple event types:

Response Fields

FieldTypeDescription
eventoneofOne of: heartbeat, snapshot, or update
session_idstrUnique session identifier for this stream
processed_sent_timeTimestampServer timestamp when response was sent

1. Heartbeat Messages

Keep-alive messages to confirm connection health.
if response.HasField('heartbeat'):
    print(f"[{datetime.now().strftime('%H:%M:%S')}] Heartbeat received")

2. Snapshot Messages

Initial state of all matching orders when subscription starts.
if response.HasField('snapshot'):
    snapshot = response.snapshot
    print(f"Snapshot received: {len(snapshot.orders)} orders")

    for order in snapshot.orders:
        print(f"  Order ID: {order.id}")
        print(f"  Symbol: {order.symbol}")
        print(f"  Side: {orders_pb2.Side.Name(order.side)}")
        print(f"  State: {orders_pb2.OrderState.Name(order.state)}")

3. Update Messages

Real-time order updates and executions.
if response.HasField('update'):
    update = response.update

    # Process executions
    for execution in update.executions:
        print(f"Execution: {orders_pb2.ExecutionType.Name(execution.type)}")

    # Process cancel rejects
    if update.HasField('cancel_reject'):
        print(f"Cancel rejected: {orders_pb2.CxlRejReason.Name(update.cancel_reject.reject_reason)}")

Order Message Structure

Core Order Fields

FieldTypeDescription
idstrExchange-assigned order ID
clord_idstrClient-assigned order ID (from your order request)
symbolstrTrading symbol
sideSideSIDE_BUY or SIDE_SELL
typeOrderTypeOrder type (LIMIT, MARKET_TO_LIMIT, etc.)
stateOrderStateCurrent order state
accountstrTrading account
order_qtyintOriginal order quantity
priceintOrder price (÷ price_scale for decimal)
cum_qtyintCumulative filled quantity
leaves_qtyintRemaining unfilled quantity
avg_pxintAverage fill price (÷ price_scale)
insert_timeTimestampWhen order was accepted
create_timeTimestampWhen order was created
Price Fields:
  • price, avg_px, stop_price are int64 values
  • Divide by the instrument’s price_scale to get decimal prices
  • price_scale is included in order responses for convenience
price = order.price / order.price_scale
print(f"Price: ${price:.4f}")

Order States

StateValueDescription
ORDER_STATE_NEW1Order accepted, resting in book
ORDER_STATE_PARTIALLY_FILLED2Order partially executed
ORDER_STATE_FILLED3Order completely filled
ORDER_STATE_CANCELED4Order canceled (leaves_qty = 0)
ORDER_STATE_REPLACED5Order modified/replaced
ORDER_STATE_PENDING_CANCEL6Cancel request pending
ORDER_STATE_REJECTED7Order rejected by exchange
ORDER_STATE_PENDING_REPLACE8Replace request pending
ORDER_STATE_EXPIRED9Order expired (e.g., Day order at end of day)
from connamara.ep3.orders.v1beta1 import orders_pb2

# Get state name
state_name = orders_pb2.OrderState.Name(order.state)
print(f"State: {state_name}")

Order Sides

SideValue
SIDE_BUY1
SIDE_SELL2
side_name = orders_pb2.Side.Name(order.side)
print(f"Side: {side_name}")

Order Types

TypeValueDescription
ORDER_TYPE_LIMIT2Limit order with specified price
ORDER_TYPE_MARKET_TO_LIMIT1Market order that converts to limit
ORDER_TYPE_STOP3Stop order
ORDER_TYPE_STOP_LIMIT4Stop-limit order

Execution Message Structure

Executions represent order lifecycle events (new, fill, cancel, reject).

Execution Fields

FieldTypeDescription
idstrUnique execution ID
typeExecutionTypeType of execution event
orderOrderCurrent order state after this execution
last_sharesintQuantity filled in this execution (for fills)
last_pxintPrice of this fill (÷ price_scale)
trade_idstrTrade ID (for fills)
aggressorboolTrue if you were the aggressor in the trade
transact_timeTimestampWhen execution occurred
textstrFree-form text (e.g., reject reason)
order_reject_reasonOrdRejectReasonRejection reason (if rejected)

Execution Types

TypeValueDescription
EXECUTION_TYPE_NEW1Order accepted (confirmed)
EXECUTION_TYPE_PARTIAL_FILL2Partial fill occurred
EXECUTION_TYPE_FILL3Complete fill (order fully executed)
EXECUTION_TYPE_TRADE9Trade report (same as fill)
EXECUTION_TYPE_CANCELED4Order canceled
EXECUTION_TYPE_REJECTED7Order rejected
EXECUTION_TYPE_REPLACED5Order modified
EXECUTION_TYPE_EXPIRED10Order expired

Order Reject Reasons

ReasonValueDescription
ORD_REJECT_REASON_UNKNOWN_SYMBOL1Symbol not found
ORD_REJECT_REASON_EXCHANGE_CLOSED2Market is closed
ORD_REJECT_REASON_ORDER_EXCEEDS_LIMIT3Exceeds risk limits
ORD_REJECT_REASON_DUPLICATE_ORDER4Duplicate clord_id
ORD_REJECT_REASON_UNKNOWN_ACCOUNT5Account not found
ORD_REJECT_REASON_INCORRECT_QUANTITY6Invalid quantity
ORD_REJECT_REASON_INVALID_PRICE7Invalid price
ORD_REJECT_REASON_OTHER99Other reason (see text field)

Complete Example (from order_stream.py)

This example matches the implementation from the Python examples repository:
import grpc
import requests
from datetime import datetime
from typing import Optional
from connamara.ep3.v1beta1 import order_entry_pb2
from connamara.ep3.v1beta1 import order_entry_pb2_grpc
from connamara.ep3.orders.v1beta1 import orders_pb2


class PolymarketOrderStreamer:
    def __init__(self, base_url: str = "https://rest.preprod.polymarketexchange.com",
                 grpc_server: str = "traderapi.us-east-1.privatelink.preprod.polymarketexchange.com:443"):
        self.base_url = base_url
        self.grpc_server = grpc_server
        self.access_token: Optional[str] = None
        self.refresh_token: Optional[str] = None
        self.access_expiration: Optional[datetime] = None
        self.session_id: Optional[str] = None

    def login(self, username: str, password: str) -> dict:
        """Authenticate with the Polymarket API and store tokens."""
        url = f"{self.base_url}/auth/v1beta1/login"
        headers = {
            "accept": "application/json",
            "Content-Type": "application/json"
        }
        data = {
            "username": username,
            "password": password
        }

        response = requests.post(url, headers=headers, json=data)
        response.raise_for_status()

        token_data = response.json()
        self.access_token = token_data["access_token"]
        self.refresh_token = token_data["refresh_token"]
        self.access_expiration = datetime.fromisoformat(
            token_data["access_expiration_time"].replace("Z", "+00:00")
        )

        return token_data

    def stream_orders(self, symbols: list = None, accounts: list = None, snapshot_only: bool = False):
        """Stream order updates using gRPC."""
        if not self.access_token:
            raise ValueError("Not authenticated. Please login first.")

        # Create credentials
        credentials = grpc.ssl_channel_credentials()

        # Create channel
        channel = grpc.secure_channel(self.grpc_server, credentials)

        # Create stub
        stub = order_entry_pb2_grpc.OrderEntryAPIStub(channel)

        # Create request
        request = order_entry_pb2.CreateOrderSubscriptionRequest(
            symbols=symbols or [],
            accounts=accounts or [],
            snapshot_only=snapshot_only
        )

        # Set up metadata with authorization
        metadata = [
            ('authorization', self.access_token)
        ]

        try:
            print(f"Starting order stream")
            print(f"Symbols: {symbols or 'ALL'}")
            print(f"Accounts: {accounts or 'ALL'}")
            print(f"Snapshot only: {snapshot_only}")
            print("-" * 60)

            # Start streaming
            response_stream = stub.CreateOrderSubscription(request, metadata=metadata)

            for response in response_stream:
                self._process_order_response(response)

        except grpc.RpcError as e:
            print(f"gRPC error: {e.code()} - {e.details()}")
            raise
        except KeyboardInterrupt:
            print("\nStream interrupted by user")
        finally:
            channel.close()

    def _process_order_response(self, response):
        """Process and display order response."""
        # Capture session ID on first message
        if response.session_id and not self.session_id:
            self.session_id = response.session_id
            print(f"\n[{datetime.now().strftime('%H:%M:%S')}] Session established")
            print(f"  Session ID: {self.session_id}")
            print("-" * 60)

        if response.HasField('heartbeat'):
            print(f"[{datetime.now().strftime('%H:%M:%S')}] Heartbeat received")

        elif response.HasField('snapshot'):
            snapshot = response.snapshot
            print(f"\n[{datetime.now().strftime('%H:%M:%S')}] Order Snapshot")
            print(f"  Total orders: {len(snapshot.orders)}")

            for order in snapshot.orders:
                self._display_order(order)

            print("-" * 60)

        elif response.HasField('update'):
            update = response.update
            print(f"\n[{datetime.now().strftime('%H:%M:%S')}] Order Update")

            # Display executions
            if update.executions:
                print(f"  Executions: {len(update.executions)}")
                for execution in update.executions:
                    self._display_execution(execution)

            # Display cancel rejects
            if update.HasField('cancel_reject'):
                self._display_cancel_reject(update.cancel_reject)

            print("-" * 60)

    def _display_order(self, order):
        """Display order details."""
        print(f"  Order ID: {order.id}")
        print(f"    Client Order ID: {order.clord_id}")
        print(f"    Symbol: {order.symbol}")
        print(f"    Side: {orders_pb2.Side.Name(order.side)}")
        print(f"    Type: {orders_pb2.OrderType.Name(order.type)}")
        print(f"    State: {orders_pb2.OrderState.Name(order.state)}")

        if order.price > 0:
            price = order.price / order.price_scale
            print(f"    Price: ${price:.4f}")

        print(f"    Order Qty: {order.order_qty}")
        print(f"    Filled Qty: {order.cum_qty}")
        print(f"    Remaining Qty: {order.leaves_qty}")

        if order.avg_px > 0:
            avg_px = order.avg_px / order.price_scale
            print(f"    Avg Price: ${avg_px:.4f}")

        if order.account:
            print(f"    Account: {order.account}")

        print()

    def _display_execution(self, execution):
        """Display execution details."""
        print(f"  Execution ID: {execution.id}")
        print(f"    Type: {orders_pb2.ExecutionType.Name(execution.type)}")

        if execution.HasField('order'):
            order = execution.order
            print(f"    Order ID: {order.id}")
            print(f"    Symbol: {order.symbol}")
            print(f"    Side: {orders_pb2.Side.Name(order.side)}")
            print(f"    State: {orders_pb2.OrderState.Name(order.state)}")

        if execution.last_shares > 0:
            print(f"    Last Shares: {execution.last_shares}")

        if execution.last_px > 0:
            last_px = execution.last_px / execution.order.price_scale
            print(f"    Last Price: ${last_px:.4f}")

        if execution.trade_id:
            print(f"    Trade ID: {execution.trade_id}")

        if execution.text:
            print(f"    Text: {execution.text}")

        if execution.order_reject_reason != orders_pb2.ORD_REJECT_REASON_UNDEFINED:
            print(f"    Reject Reason: {orders_pb2.OrdRejectReason.Name(execution.order_reject_reason)}")

        print()

    def _display_cancel_reject(self, cancel_reject):
        """Display cancel reject details."""
        print(f"  Cancel Reject:")
        print(f"    ID: {cancel_reject.id}")
        print(f"    Client Order ID: {cancel_reject.clord_id}")
        print(f"    Original Client Order ID: {cancel_reject.orig_clord_id}")
        print(f"    Response To: {orders_pb2.CxlRejResponseTo.Name(cancel_reject.response_to)}")
        print(f"    Reject Reason: {orders_pb2.CxlRejReason.Name(cancel_reject.reject_reason)}")
        if cancel_reject.text:
            print(f"    Text: {cancel_reject.text}")
        print()


# Usage
if __name__ == "__main__":
    streamer = PolymarketOrderStreamer()

    # Login
    streamer.login("your_username", "your_password")

    # Stream orders
    streamer.stream_orders(
        symbols=["mlb-ari-sf-2025-09-08"],
        accounts=[]
    )

Sample Output

Starting order stream
Symbols: ['mlb-ari-sf-2025-09-08']
Accounts: ALL
Snapshot only: False
------------------------------------------------------------

[14:30:15] Session established
  Session ID: session_abc123
------------------------------------------------------------

[14:30:15] Order Snapshot
  Total orders: 3

  Order ID: order_12345
    Client Order ID: clord_abc
    Symbol: mlb-ari-sf-2025-09-08
    Side: SIDE_BUY
    Type: ORDER_TYPE_LIMIT
    State: ORDER_STATE_NEW
    Price: $0.525
    Order Qty: 1000
    Filled Qty: 0
    Remaining Qty: 1000

------------------------------------------------------------
[14:30:45] Heartbeat received

[14:31:02] Order Update
  Executions: 1

  Execution ID: exec_67890
    Type: EXECUTION_TYPE_PARTIAL_FILL
    Order ID: order_12345
    Symbol: mlb-ari-sf-2025-09-08
    Side: SIDE_BUY
    State: ORDER_STATE_PARTIALLY_FILLED
    Last Shares: 300
    Last Price: $0.525
    Trade ID: trade_xyz

------------------------------------------------------------

Order Lifecycle

Typical Order Flow

1. NEW          → Order accepted, resting in book
2. PARTIAL_FILL → First partial fill
3. PARTIAL_FILL → Additional partial fills (if any)
4. FILL         → Final fill, order complete

Cancel Flow

1. NEW              → Order resting
2. PENDING_CANCEL   → Cancel request received
3. CANCELED         → Cancel confirmed

Reject Flow

1. REJECTED → Order rejected immediately

Next Steps