# Positions & Orders

Base URL: `https://service.leverup.xyz`

These endpoints return indexed data across **all markets** in one request, which is the main reason
to prefer them over [`getPositionsV4`](/onchain/reading-data#getpositionsv4) — the contract read takes
one `pairBase` at a time. They also cover closed positions, which the contract does not retain.

The trade-off is indexing lag: a position appears here a moment after it appears onchain. For
anything latency-sensitive, read the contract.

## Open positions

```http
GET /v1/user/{user_address}/open-positions
```

| Parameter | Default | Notes |
| :--- | :--- | :--- |
| `block_chain` | `MONAD` | |
| `page` | `0` | |
| `size` | `20` | Capped at 100 |

### Response

```json
{
  "content": [
    {
      "positionHash": "0x…",
      "pair": "BTC/USD",
      "pairBase": "0xcf5a…",
      "trader": "0x…",
      "tokenIn": "0x7547…",
      "marginToken": "0xfd44…",
      "isLong": true,
      "margin": "9930000000000000000",
      "qty": "10000000",
      "entryPrice": "100123450000000000000000",
      "stopLoss": "0",
      "takeProfit": "0",
      "openFee": "70000000000000000",
      "executionFee": "0",
      "longAccFundingFeePerShare": "0",
      "holdingFeeRate": "0",
      "timestamp": 1785312000,
      "status": "OPEN",
      "closeInfo": null
    }
  ],
  "pageNumber": 0,
  "pageSize": 20,
  "totalPages": 1,
  "totalElements": 1
}
```

| Field | Unit |
| :--- | :--- |
| `qty` | 1e10 |
| `entryPrice`, `stopLoss`, `takeProfit` | 1e18 |
| `margin`, `openFee`, `executionFee` | `marginToken` decimals — 18 for LVUSD and LVMON |
| `holdingFeeRate` | 1e12 |
| `longAccFundingFeePerShare` | 1e18, signed |
| `timestamp` | Unix seconds |

`marginToken` is the `lvToken`. Every amount above is denominated in it, not in `tokenIn`.

::: info Accrued fees are not here
This endpoint returns the position as indexed at open time. Live accrued funding and holding fees
come from [`getPositionsV4`](/onchain/reading-data#getpositionsv4) onchain.
:::

## All positions

Open, closing, closed and cancelled.

```http
GET /v1/user/{user_address}/positions
```

| Parameter | Default | Notes |
| :--- | :--- | :--- |
| `block_chain` | `MONAD` | |
| `page` | `0` | |
| `size` | `20` | Capped at 100 |
| `timestamp_after` | — | Only positions opened after this Unix second |
| `close_time_after` | — | Only positions closed after this Unix second |

Same shape as above, plus a populated `closeInfo` for closed positions:

```json
{
  "positionHash": "0x…",
  "status": "CLOSED",
  "closeInfo": {
    "closePrice": "101500000000000000000000",
    "fundingFee": "-1200000000000000",
    "closeFee": "70000000000000000",
    "pnl": "1370000000000000000",
    "holdingFee": "500000000000000",
    "closeTime": 1785315600
  }
}
```

| `status` | Meaning |
| :--- | :--- |
| `PENDING` | Requested, not yet filled |
| `OPEN` | Live |
| `CLOSING` | Close requested, not yet settled |
| `CLOSED` | Settled |
| `CANCELLED` | Never opened — refunded |

`closeInfo` is present for `CLOSED`, and for `CLOSING` once the PnL is known. `fundingFee` and `pnl`
are signed. All amounts use `marginToken` decimals.

```ts
async function realizedPnl(address: string, sinceUnix: number) {
  const res = await fetch(
    `${API}/v1/user/${address}/positions?close_time_after=${sinceUnix}&size=100`,
  )
  const { content } = await res.json()

  return content
    .filter((p: any) => p.status === 'CLOSED')
    .reduce((sum: bigint, p: any) => sum + BigInt(p.closeInfo.pnl), 0n)
}
```

## Limit orders

There is no public per-user limit-order endpoint. Read them onchain with
[`getLimitOrders`](/onchain/reading-data#getlimitorders), and TP/SL orders with
[`getTraderDecreaseOrders`](/onchain/tpsl-orders#reading-orders).

Order lifecycle events do appear in [trade history](/api/history#trade-history), so a completed order
is visible there after the fact.
