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(Position) returns (stream BlockTwapStatuses) {}Response Stream
message BlockTwapStatuses {
// JSON-encoded block envelope from "node_twap_statuses_by_block".
bytes data = 1;
}The data field contains a JSON-encoded block envelope with:
- 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 on-disk 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.
Messages arrive in strictly increasing block_number order with no gaps and no duplicates. The stream is stateless: reconnect by passing the next block_height (or a timestamp_ms) in the Position and resume exactly where you left off. Both are inclusive; an empty or zero Position targets the latest data.
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 on-disk block-envelope record. block_numbercorresponds toabci_block.roundin 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
import grpc
import json
import os
from decimal import Decimal
import hyperliquid_pb2
import hyperliquid_pb2_grpc
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'],
}
def stream_twap_statuses():
endpoint = os.getenv('HYPERLIQUID_ENDPOINT')
api_key = os.getenv('API_KEY')
credentials = grpc.ssl_channel_credentials()
options = [('grpc.max_receive_message_length', 150 * 1024 * 1024)] # 150MB max
metadata = [('x-api-key', api_key)]
with grpc.secure_channel(endpoint, credentials, options=options) as channel:
client = hyperliquid_pb2_grpc.HyperliquidL1GatewayStub(channel)
# Empty Position means latest; set block_height or timestamp_ms to backfill
request = hyperliquid_pb2.Position()
twaps = {}
for response in client.StreamTwapStatuses(request, metadata=metadata):
block = json.loads(response.data)
apply_twap_status(block, twaps)Error Handling and Reconnection
Reconnect by replaying from the last processed block. Pass the next block_height in the Position so no events are skipped or duplicated.
def stream_with_resume(client, metadata, start_block):
next_block = start_block
while True:
request = hyperliquid_pb2.Position(block_height=next_block)
try:
for response in client.StreamTwapStatuses(request, metadata=metadata):
block = json.loads(response.data)
process(block)
next_block = block['block_number'] + 1
except grpc.RpcError as e:
print(f"stream error, resuming from {next_block}: {e}")Best Practices
- Connection Management: Implement robust reconnection logic with exponential backoff
- Resumable State: Track the last processed
block_numberso you can resume from the next block - 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
- Data Retention: Historical availability follows the gateway's retained on-disk feed; pruned positions return
NotFound - 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.