PearPear
API IntegrationTrade Activity

Closed Position

Read closed-trade history, with realized PnL, fees, and funding per leg.

GET /positions/closed (SDK: sdk.core.positions.closed) returns closed trades, newest close first. Pear composes each one server-side into the trade a trader recognises: what it earned, what it cost, and what each leg did.

curl "https://pro-gateway.pearprotocol.io/positions/closed?limit=50" \
  -H "x-api-key: $PEAR_API_KEY" \
  -H "x-trade-account-id: $TRADE_ACCOUNT_ID"

Response

{
  "limit": 50,
  "nextCursor": "eyJjbG9zZWRBdCI6…",
  "trades": [
    {
      "positionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "connector": "hyperliquid",
      "openedAt": "2026-01-01T00:00:00.000Z",
      "closedAt": "2026-01-03T11:42:07.000Z",
      "closedReason": "USER",
      "origin": "PEAR",
      "grossPnl": "+42.50",
      "netPnl": "+41.72",
      "totalFees": "+0.78",
      "tradeFees": "+0.61",
      "pearFees": "+0.17",
      "totalFunding": "-0.41",
      "entryNotional": "1000.00",
      "exitNotional": "1042.50",
      "exitNotionalComplete": true,
      "hasSyntheticFills": false,
      "unstableHistoricalData": false,
      "longAssets": ["0"],
      "shortAssets": ["1"],
      "legs": [
        {
          "asset": "0",
          "side": "LONG",
          "entryVwap": "96150.5",
          "exitVwap": "98420.0",
          "grossPnl": "+118.00",
          "netPnl": "+117.55",
          "totalFees": "+0.45",
          "tradeFees": "+0.35",
          "pearFees": "+0.10",
          "funding": "-1.25",
          "entryNotional": "500.00",
          "exitQuantity": "0.0052",
          "undeterminedSize": "0"
        },
        {
          "asset": "1",
          "side": "SHORT",
          "entryVwap": "3086.2",
          "exitVwap": "3552.0",
          "grossPnl": "-75.50",
          "netPnl": "-75.83",
          "totalFees": "+0.33",
          "tradeFees": "+0.26",
          "pearFees": "+0.07",
          "funding": "+0.84",
          "entryNotional": "500.00",
          "exitQuantity": "0.1620",
          "undeterminedSize": "0"
        }
      ]
    }
  ]
}

longAssets, shortAssets, and legs[].asset hold instrument IDs, so "0" is BTC and "1" is ETH on Hyperliquid. See Instrument ID.

PnL, fees, and funding

FieldMeaning
grossPnlRealized PnL before fees.
netPnlRealized PnL after fees.
totalFeesSigned, so a venue maker rebate makes it negative. Split into tradeFees (the exchange) and pearFees. Each leg carries the same three fields.
totalFundingFunding over the position's life. Negative means you paid.

totalFunding is not folded into netPnl. netPnl is a fills-only number, and funding is a separate cash flow. Add the two for the trade's all-in result:

all-in = netPnl + totalFunding = 41.72 + (-0.41) = 41.31

totalFunding can also exceed the sum of legs[].funding. Funding follows exposure, and an asset that carried exposure but produced no confirmed fill earns no leg. Summing the legs would understate what the trade paid.

When a number is missing or partial

Closed trades are derived from a fills ledger, so a few fields tell you how much to trust the rest:

FieldRead it as
entryVwap, exitVwapAbsent, not "0", when no priced entry or exit exists. "0" is a real price, so absence is the honest answer.
undeterminedSizeSize that closed with no derivable PnL. It contributes nothing to grossPnl or exitVwap. The leg's total closed size is exitQuantity + undeterminedSize.
exitNotionalCompletefalse when part of the position closed unpriceably, so exitNotional understates the proceeds.
hasSyntheticFillstrue when a synthetic bridge fill reconstructed a gap in the ledger. This is not the opposite of exitNotionalComplete: a synthetic fill can carry a real price and stay complete.
unstableHistoricalDatatrue when a backfilled fill made this trade's metrics unreliable.
closedReasonUSER for a close you asked for, NETTED when an opposing trade cancelled it out, LIQUIDATED for a venue liquidation. The key is omitted, not null, when no reason was recorded.

Step history

Add includeSteps=true for what happened to the position over its life, oldest first: the open, every add, every reduction, and the close.

{
  "steps": [
    {
      "executionId": "8d2e…",
      "at": "2026-01-01T00:00:00.000Z",
      "grossPnl": "+0",
      "legs": [
        { "kind": "INCREASE", "asset": "0", "side": "BUY", "quantity": "0.0052", "price": "96150.5", "tradeFee": "+0.22", "pearFee": "+0.05" }
      ]
    }
  ]
}

legs says how each asset finished; steps says how it got there. A step leg is INCREASE, REDUCE, or REDUCE_UNPRICED. Only reductions book PnL, so grossPnl is "+0" on a pure increase.

A REDUCE leg also carries entryBasis: the running cost basis that reduction closed against. Its grossPnl is (price - entryBasis) * quantity, signed by direction.

Ask for steps only when you need them. The array is unbounded, one entry per recorded action, so a heavily traded position produces a long one.

Paginating

Filter by startDate and endDate, and page with limit and cursor.

ParameterValues
startDate, endDateInclusive bounds on the close time. Absent means unbounded.
limitDefault 20, maximum 200. This is deliberately below the 2000 other lists allow, because every trade drags its fills along. limit=500 is a 400.
cursorThe nextCursor of the previous page.
includeStepstrue adds the step history. Unlike the flags on GET /executions, includeSteps=false does work.

This endpoint takes no connector, no status, and no orderBy. Ordering is fixed at newest-closed-first.

Page until nextCursor is absent, never until a page comes back short. A page can hold fewer than limit trades and still have more behind it, because closed positions with no confirmed fills are skipped.

Live positions are at Open Position. The fills behind a closed trade are at Fills. The same trades aggregated across the whole account are at Portfolio.

On this page