Strategy Status Stream
Real-time WebSocket stream of strategy lifecycle events: deployments, pauses, position open/close, order placement and fills, and unrecoverable errors. Use this stream to drive dashboards, audit logs, or downstream automation.
Subscription
After connecting, send a subscribe frame referencing one or more strategy IDs:
{ "op": "subscribe", "channel": "strategy.status", "id": "strat_8f2a1b" }
To receive events for every strategy on the account, subscribe with the wildcard:
{ "op": "subscribe", "channel": "strategy.status", "id": "*" }
Unsubscribe with the symmetric frame:
{ "op": "unsubscribe", "channel": "strategy.status", "id": "strat_8f2a1b" }
The server replies with an ack:
{ "op": "ack", "channel": "strategy.status", "id": "strat_8f2a1b", "ok": true }
Authentication
WebSocket connections must be authenticated using the same HMAC-SHA256 scheme as REST. You may either:
- Sign the connection URL — pass
api_key,timestamp, andsignatureas query-string parameters where the canonical string istimestamp + "GET" + path. Example:wss://stream.pipai.example/v1/ws?api_key=...×tamp=...&signature=.... - Send an
authframe as the first message after connecting:{ "op": "auth", "api_key": "...", "timestamp": 1761739200000, "signature": "..." }
Connections that do not authenticate within 5 seconds are closed. See Authentication for the full signing scheme.
Event types
| Event | When it fires |
|---|---|
deployed | Strategy transitioned to deployed. |
paused | Strategy transitioned to paused. |
position_opened | A new position was opened by the strategy. |
position_closed | A position was closed (by exit rule, manual pause-with-close, or stop-loss). |
order_placed | An order was submitted to the exchange. |
order_filled | An order was fully or partially filled. |
order_cancelled | An order was cancelled by the engine or the exchange. |
error | The engine halted with an unrecoverable error. The strategy transitions to error. |
Payload schema
Every event uses the same envelope:
{
"channel": "strategy.status",
"event": "position_opened",
"strategy_id": "strat_8f2a1b",
"seq": 12483,
"ts": "2026-04-29T12:00:00.000Z",
"data": { }
}
| Envelope field | Type | Description |
|---|---|---|
channel | string | Always "strategy.status". |
event | string | One of the event types above. |
strategy_id | string | Strategy that emitted the event. |
seq | integer | Monotonically increasing per-channel sequence number. Use to detect gaps and to resume after reconnect. |
ts | string | ISO 8601 UTC timestamp with millisecond precision. |
data | object | Event-specific payload, schema below. |
position_opened
{
"channel": "strategy.status",
"event": "position_opened",
"strategy_id": "strat_8f2a1b",
"seq": 12483,
"ts": "2026-04-29T12:00:00.000Z",
"data": {
"position_id": "pos_4ab9c0",
"symbol": "BTCUSDT",
"side": "long",
"entry_price": "67234.50",
"quantity": "0.0150",
"leverage": 3,
"notional": "1008.52"
}
}
position_closed
{
"channel": "strategy.status",
"event": "position_closed",
"strategy_id": "strat_8f2a1b",
"seq": 12519,
"ts": "2026-04-29T13:42:11.250Z",
"data": {
"position_id": "pos_4ab9c0",
"symbol": "BTCUSDT",
"side": "long",
"entry_price": "67234.50",
"exit_price": "67890.10",
"quantity": "0.0150",
"realized_pnl": "9.83",
"reason": "exit_rule"
}
}
reason is one of exit_rule, stop_loss, take_profit, manual_pause_close, liquidation.
order_filled
{
"channel": "strategy.status",
"event": "order_filled",
"strategy_id": "strat_8f2a1b",
"seq": 12491,
"ts": "2026-04-29T12:00:00.412Z",
"data": {
"order_id": "ord_77e2b1",
"position_id": "pos_4ab9c0",
"symbol": "BTCUSDT",
"side": "buy",
"type": "limit",
"price": "67234.50",
"quantity": "0.0150",
"filled_quantity": "0.0150",
"fee": "0.5039",
"fee_currency": "USDT"
}
}
Partial fills emit one event per fill; filled_quantity is cumulative on the order.
error
{
"channel": "strategy.status",
"event": "error",
"strategy_id": "strat_8f2a1b",
"seq": 12604,
"ts": "2026-04-29T14:05:33.001Z",
"data": {
"code": "EXCHANGE_AUTH_FAILED",
"message": "Exchange API key rejected by Binance",
"fatal": true
}
}
When fatal is true, the strategy has transitioned to error status and will not recover automatically — investigate, fix, and redeploy. Non-fatal errors are emitted for visibility but do not change strategy status.
Reconnection
Connections may drop for a variety of reasons (network, server maintenance, idle timeout). Clients should reconnect with exponential backoff:
- Start with a 1 s delay.
- Double on each consecutive failure, jittered by ±25%.
- Cap at 30 s.
- Reset to 1 s after a successful connection that stays up for 60 s.
To resume without gaps, include the last received seq in the subscribe frame:
{ "op": "subscribe", "channel": "strategy.status", "id": "strat_8f2a1b", "since_seq": 12483 }
The server replays buffered events with seq > since_seq. Replay is best-effort: events are buffered for up to 5 minutes. If since_seq is older than the buffer, the server responds with { "op": "ack", "ok": false, "code": "REPLAY_UNAVAILABLE" } and you should reconcile state via Get Strategy.