Open Position
List open basket positions.
List open basket positions with GET /positions (SDK: sdk.core.positions.list), or a smaller payload with GET /positions/lite (sdk.core.positions.openLite).
curl "https://pro-gateway.pearprotocol.io/positions?status=OPEN&limit=50" \
-H "x-api-key: $PEAR_API_KEY" \
-H "x-trade-account-id: $TRADE_ACCOUNT_ID"Response
{
"limit": 50,
"positions": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"tradeAccountId": "9c1b2d3e-4f56-4789-a0b1-c2d3e4f56789",
"connector": "hyperliquid",
"status": "OPEN",
"origin": "PEAR",
"exposure": { "0": "+0.0052", "1": "-0.1620" },
"initialExposureUSD": { "0": "+500", "1": "-500" },
"entryPrices": { "0": "96150.5", "1": "3086.2" },
"totalFundingPayment": { "0": "-1.25", "1": "+0.84" },
"unstableHistoricalData": false,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T04:12:33.000Z"
}
]
}Reading the maps
exposure, initialExposureUSD, entryPrices, and totalFundingPayment are all keyed by instrument ID, not by ticker. In the example, "0" is BTC and "1" is ETH on Hyperliquid. Resolve the keys with GET /instruments/symbol-to-id-map, and see Instrument ID.
| Field | Meaning |
|---|---|
exposure | Signed size per instrument. Positive is long, negative is short. |
initialExposureUSD | Signed USD size when the position opened. It does not move with adjustments. |
entryPrices | Weighted-average entry price per instrument, from the fills ledger. It does move with adjustments. |
totalFundingPayment | Cumulative funding in USD per instrument. Negative means you paid. |
origin | PEAR if only Pear fills built the position, EXTERNAL if only outside fills did, PARTIAL for a mix. |
unstableHistoricalData | true when a backfilled fill invalidated metrics derived from this position. |
closedReason | Present only on a closed position: USER, NETTED, or LIQUIDATED. Omitted, not null, when no reason was recorded. |
closedAt | When the position closed. Present only on a closed position. |
Each position is basket-level: its PnL, entry, and size reflect the basket, not the exchange's per-asset view. See Synthetic Position.
Query parameters
| Parameter | Values |
|---|---|
status | OPEN or CLOSED. Defaults to OPEN. |
orderBy | -createdAt (the default), createdAt, -updatedAt, or updatedAt. |
limit | Default 20, maximum 2000. |
cursor | The nextCursor of the previous page. |
connector | Accepted and ignored. The venue comes from the trade account. |
Omitting status returns open positions only. There is no "everything" default here. Ask for status=CLOSED to see closed rows.
statusis single-valued in practice. Asking for both —?status=OPEN,CLOSED— returns zero rows, because the two are combined as and rather than or. Make two calls, or read closed history fromGET /positions/closed, which composes each closed trade properly.
orderBy also changes what the cursor means: the cursor is the value of whichever column the ordering uses, so a page taken under -createdAt cannot be continued under -updatedAt. Keep orderBy fixed for the whole walk.
nextCursorhere is a bare timestamp with no tiebreaker. Two positions created in the same instant sit on a page boundary that can drop or repeat a row.GET /positions/closeduses a compound cursor and does not have this problem.
The lite list
GET /positions/lite takes no query parameters, does not page, and returns only open positions:
{
"positions": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"positionKey": "9f2c4a7e10b3486d5c8e1a04b7d92f63c05e8a1b7d34f62089ac5e1f30b74d28",
"createdAt": "2026-01-01T00:00:00.000Z",
"exposure": { "0": "+0.0052", "1": "-0.1620" },
"status": "OPEN",
"origin": "PEAR",
"connector": "hyperliquid"
}
]
}positionKey is a hex string derived from the trade account and the basket's instruments, so the same basket on the same account always produces the same key. It is the field that joins a position to the schedules and ladders working on the same basket. It appears on no other position read. Treat it as an opaque identifier. See Position Key.
Use this endpoint when you are polling for state changes and do not need entry prices or funding, both of which the full list computes on every request.
Live updates
Subscribe to user_events on the WebSocket and read position.created, position.updated, and position.closed instead of polling. Closed positions stay readable through GET /positions/closed.