StreamTwapStatuses - Real-time TWAP Status Streaming
Stream TWAP order lifecycle events from Hyperliquid L1 Gateway via gRPC. Track TWAP status changes and execution progress per block in real time.
Stream TWAP order lifecycle events starting from a position, providing real-time access to TWAP state updates across Hyperliquid.
Full Code Examples
Clone our gRPC Code Examples Repository for complete, runnable implementations in Go, Python, and Node.js.
When to Use This Method
StreamTwapStatuses is essential for:
- TWAP Lifecycle Tracking - Follow TWAP orders as their state changes
- Execution Monitoring - Track executed size and notional over time
- Strategy Analytics - Reconstruct TWAP execution progress across blocks
- Compliance and Auditing - Record TWAP status transitions with block alignment
Pairs with TWAP Actions
StreamTwapStatuses reports lifecycle changes after the order exists on HyperCore. To decode the signed request that created or canceled a TWAP, pair this stream with twapOrder and twapCancel actions from StreamBlocks.
Method signature
rpc StreamTwapStatuses(StreamRequest) returns (stream Record) {}V3 accepts an inclusive position.block_number or Unix-millisecond position.timestamp. Explicit zero is invalid. A positioned call replays inclusively and then tails live; an empty request tails newly written TWAP status records.
filter: {
fields: {
field: "coins"
op: FILTER_OP_IN
values: "BTC"
}
}TWAP statuses accept users and coins filters. User matching is case-insensitive and coin matching is case-sensitive; values within a field use OR semantics and separate fields use AND semantics. Records with zero matching events are skipped. Each Record contains optional block_number and timestamp cursors plus the filtered envelope in data. A pruned position returns NOT_FOUND; malformed filters return INVALID_ARGUMENT.
Response Stream
V3 carries the JSON-encoded node_twap_statuses_by_block envelope in Record.data.
Decode the data field to access:
- Block reference (
local_time,block_time,block_number) - An
eventsarray of TWAP status records - A TWAP state object behind each lifecycle update
Per-Block Delivery
StreamTwapStatuses emits one message per source block-envelope record, including records with an empty events array. Most blocks do not contain a TWAP lifecycle update, so empty envelopes are normal. They still let consumers advance their cursor and confirm exactly which block was last processed.
V3 clients must detect discontinuities and duplicates and persist only cursors present on Record. Reconnect with StreamRequest.position.block_number or position.timestamp; replay is inclusive.
Full TWAP Status Spec
StreamTwapStatuses emits a JSON payload matching Hyperliquid's node_twap_statuses_by_block format, passed through byte-for-byte from the node with no normalization. The envelope is the same block-envelope shape as fills, but events holds TWAP lifecycle objects rather than fill tuples.
Top-level keys (always present):
{
"local_time": "2026-07-03T06:00:14.508835456",
"block_time": "2026-07-03T06:00:14.057132297",
"block_number": 1058646974,
"events": [
{
"time": "2026-07-03T06:00:14.057132297",
"twap_id": 1999381,
"state": {
"coin": "kPEPE",
"user": "0x2ee1f7d5650bb9bc08e101dc5ab2300b4f5c1be9",
"side": "B",
"sz": "555555.0",
"executedSz": "555555.0",
"executedNtl": "1371.51378",
"minutes": 5,
"reduceOnly": false,
"randomize": false,
"timestamp": 1783058112775
},
"status": "finished"
}
]
}Status event fields (events[i]):
| Field | Type | Description |
|---|---|---|
time | string | Event timestamp in the no-Z layout. A string, not an integer millisecond value |
twap_id | number | TWAP order identifier |
state | object | TWAP state at the lifecycle update. See TWAP State Object |
status | string or object | Lifecycle status. See Status Values |
Every event is global, not user-scoped to the API key. Filter by state.user, state.coin, or twap_id in your application when you only need a subset.
TWAP State Object
| Field | Type | Description |
|---|---|---|
coin | string | Coin identifier |
user | string | User address |
side | string | "B" for buy, "A" for sell |
sz | string | Target TWAP size as a numeric string |
executedSz | string | Executed size so far as a numeric string |
executedNtl | string | Executed notional so far as a numeric string |
minutes | number | TWAP duration in minutes |
reduceOnly | boolean | Whether the TWAP is reduce-only |
randomize | boolean | Whether the TWAP randomizes slice timing or size |
timestamp | number | TWAP creation time in Unix milliseconds |
The state object is a snapshot of the TWAP at the lifecycle event, not a fill-by-fill execution log. Use executedSz and executedNtl to measure aggregate progress for that TWAP at the event boundary. For individual trade executions, consume StreamFills and join on user, coin, and time windows according to your own strategy model.
Status Values
Observed string status values include:
| Status | Meaning |
|---|---|
activated | TWAP order became active |
finished | TWAP execution completed or otherwise reached its terminal state |
terminated | TWAP order was terminated before normal completion |
status can also be an object when the lifecycle update carries an error:
{
"status": {
"error": "Reduce only order would increase position."
}
}Treat the status set as non-exhaustive. Hyperliquid can add new lifecycle states and error payloads over time.
Lifecycle Examples
An activated event starts a TWAP lifecycle. At activation, executedSz and executedNtl are typically zero:
{
"time": "2026-07-03T06:00:30.373151284",
"twap_id": 1999392,
"state": {
"coin": "xyz:NBIS",
"user": "0x6f97b7de6be7b7771e975e46bae96c35e332e172",
"side": "B",
"sz": "226.72",
"executedSz": "0.0",
"executedNtl": "0.0",
"minutes": 7,
"reduceOnly": false,
"randomize": false,
"timestamp": 1783058430373
},
"status": "activated"
}A terminated event records the final observed state when a TWAP stops before normal completion. Partial execution is visible through executedSz and executedNtl:
{
"time": "2026-07-03T06:02:08.835250901",
"twap_id": 1999394,
"state": {
"coin": "xyz:NBIS",
"user": "0x6f97b7de6be7b7771e975e46bae96c35e332e172",
"side": "B",
"sz": "226.7",
"executedSz": "60.45",
"executedNtl": "13377.625",
"minutes": 7,
"reduceOnly": false,
"randomize": false,
"timestamp": 1783058434767
},
"status": "terminated"
}Guarantees and alignment:
eventscontains TWAP status records from the block across all users.- Messages arrive in strictly increasing
block_number, one per source block-envelope record. block_numberis sequential and distinct from the potentially skippingabci_block.roundconsensus value in StreamBlocks;block_timealigns withabci_block.time.
Developer tips:
- Use
twap_idas the primary key for TWAP lifecycle state. - Parse
sz,executedSz, andexecutedNtlas decimals to avoid precision loss. - Treat
timeas a formatted string, distinct from the integer-millisecondtimestampinsidestate. - Handle empty
eventsarrays; quiet blocks are normal. - Store the raw block envelope if you need reproducible audits; the gateway does not normalize or reshape the node payload.
Common Use Cases
1. TWAP Lifecycle Index
from decimal import Decimal
def apply_twap_status(block, index):
for event in block.get('events', []):
state = event['state']
target = Decimal(state['sz'])
executed = Decimal(state['executedSz'])
status = event['status']
status_key = status if isinstance(status, str) else 'error'
index[event['twap_id']] = {
'status': status,
'status_key': status_key,
'user': state['user'],
'coin': state['coin'],
'side': state['side'],
'target_size': target,
'executed_size': executed,
'progress': executed / target if target else Decimal('0'),
'block_number': block['block_number'],
'block_time': block['block_time'],
}2. TWAP Alert Stream
def alert_on_terminal_twaps(block):
for event in block.get('events', []):
status = event['status']
if status not in ('finished', 'terminated') and not isinstance(status, dict):
continue
state = event['state']
status_label = status.get('error', 'error') if isinstance(status, dict) else status
print(
f"{status_label} twap={event['twap_id']} "
f"user={state['user']} coin={state['coin']} "
f"executed={state['executedSz']}/{state['sz']}"
)3. Streaming TWAP Statuses in Python
Open V3 with an empty StreamRequest to tail newly written records, or set StreamRequest.position.block_number for inclusive replay. Decode each Record.data envelope and apply it with the lifecycle-index helper above.
Error Handling and Reconnection
Reconnect with StreamRequest.position.block_number set to the last committed V3 cursor. Replay is inclusive, so deduplicate that record before advancing.
Best Practices
- Connection Management: Implement robust reconnection logic with exponential backoff
- Resumable State: Follow the version-specific recovery guidance above and persist the cursor required by your selected API version
- Stable Keys: Index TWAP lifecycle state on
twap_id - Defensive Parsing: Handle empty
eventsarrays, unknown future string statuses, and object status payloads - Numeric Precision: Parse numeric strings with decimal-safe types
- Action Correlation: Join with StreamBlocks when you need the original signed
twapOrderortwapCancelaction - Selective Storage: Store terminal events and the last active state if you do not need every empty envelope
Current Limitations
- Replay History: Available replay history varies by feed and endpoint. A cursor outside retained history returns
NOT_FOUND - Schema Coverage: The upstream lifecycle set can grow; treat documented statuses as examples, not an exhaustive list
- Backpressure: High-volume periods may require careful handling to avoid overwhelming downstream systems
Resources
- GitHub: gRPC Code Examples - Complete working examples
- GetTwapStatuses - Point-in-time TWAP status retrieval
- twapOrder Action Type - TWAP order submission payloads in StreamBlocks
- twapCancel Action Type - TWAP cancellation payloads in StreamBlocks
Need help? Contact our support team or check the Hyperliquid gRPC documentation.
StreamRawBookDiffs - Real-time Book Diff Streaming
Stream incremental order book changes from Hyperliquid L1 Gateway via gRPC. Reconstruct the resting book from per-block additions, updates, and removals.
GetBlock - Get Block Data
Retrieve a single block from Hyperliquid L1 Gateway via gRPC. Get blockchain data at a specific position.