> For the complete documentation index, see [llms.txt](https://docs.hydromancer.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hydromancer.xyz/readme/rest-api/user-performance/usercompletedtradesbytime.md).

# userCompletedTradesByTime

Paginate a user's completed (fully closed) positions by close time, oldest-first.

Returns completed trades for a user with `close_time` ≥ `startTime` (or after a `cursor`), ordered **oldest-first** by `close_time` then `tx_index`. Pagination is identical to [`userFillsByTime`](/readme/rest-api/historical-data/userfillsbytime.md): pass `startTime` for the first page, then on each subsequent page pass the `cursor` `"{close_time}_{tx_index}"` built from the **last record** of the previous response. When `cursor` is supplied, `startTime` is ignored and the boundary record is excluded from the next page (no duplicates, no `+ 1` hack).

`dex` is optional. Omit to return trades from every perp DEX. Set to `"main_dex"` for the native Hyperliquid dex, or to a HIP-3 dex name (e.g. `"xyz"`) to scope the trade list to that dex.

`outcomes` is optional. Set it to `true` to return trades from HIP-4 outcome markets (coins like `#8131`) instead of perp and spot trades. The two sets are disjoint and are never mixed in one response.

Outcome records **omit** `positionType`, `fundingPnl`, `leverage` and `isCross` entirely. These are not unknown values — they do not apply. Outcome exposure is a token holding, so there is no short side, no leverage and no funding, and emitting `"Long"` or `"0"` would assert facts about the market that are not true.

On a perp or spot record all four are **always present and always carry a value**, so a client may read them directly there. Treat an absent field as "not applicable to this market type" — equivalently, as "this record is from an outcome market".

## POST Request

<table><thead><tr><th width="161.9998779296875">Field</th><th width="119">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>type</code></td><td>string</td><td>Must be <code>"userCompletedTradesByTime"</code></td></tr><tr><td><code>user</code></td><td>string</td><td>Ethereum address (0x-prefixed, 42 characters)</td></tr><tr><td><code>startTime</code></td><td>integer</td><td>Earliest <code>close_time</code> to include (Unix ms, inclusive). Skipped when <code>cursor</code> is used. Either <code>startTime</code> or <code>cursor</code> must be supplied</td></tr><tr><td><code>endTime</code></td><td>integer</td><td>Latest <code>close_time</code> to include (Unix ms, inclusive). Default: now (optional)</td></tr><tr><td><code>cursor</code></td><td>string</td><td>Composite of <code>close_time</code> and <code>tx_index</code> with <code>"_"</code> separator (e.g. <code>"1734571490123_12"</code>). Take from the <strong>last record</strong> of the previous response (optional)</td></tr><tr><td><code>builder</code></td><td>string</td><td>Filter to trades whose closing fill was routed through this builder address (last-touch attribution, 0x-prefixed). Omit (or send <code>null</code>) for no filtering. A malformed, empty or non-string value is a <code>400</code> rather than a silently unfiltered result (optional)</td></tr><tr><td><code>limit</code></td><td>integer</td><td>Max results to return. Default 100, max 500 (optional)</td></tr><tr><td><code>dex</code></td><td>string</td><td>Scope to a single perp DEX. <code>"main_dex"</code> for the native Hyperliquid dex, or a HIP-3 dex name (e.g. <code>"xyz"</code>). Omit for all dexes (optional)</td></tr><tr><td><code>outcomes</code></td><td>bool</td><td>Return HIP-4 outcome-market trades instead of perp/spot trades. Defaults to <code>false</code>. Cannot be combined with <code>dex</code>, since outcome markets are not dex-scoped. Outcome records <strong>omit</strong> <code>positionType</code>, <code>fundingPnl</code>, <code>leverage</code> and <code>isCross</code> entirely, rather than reporting placeholder values: outcome exposure is a token holding that is only ever long, is unlevered, and accrues no funding. All four are always present on perp and spot records (optional)</td></tr></tbody></table>

{% tabs %}
{% tab title="cURL" %}

```bash
# First page
curl -X POST https://api.hydromancer.xyz/info \
  -H "Authorization: Bearer $HYDROMANCER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "userCompletedTradesByTime",
    "user": "0x0000000000000000000000000000000000000000",
    "startTime": 0,
    "limit": 100
  }'

# Next page — cursor built from the last record of the previous response
curl -X POST https://api.hydromancer.xyz/info \
  -H "Authorization: Bearer $HYDROMANCER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "userCompletedTradesByTime",
    "user": "0x0000000000000000000000000000000000000000",
    "cursor": "1734571490123_12",
    "limit": 100
  }'
```

{% endtab %}

{% tab title="Python" %}

```python
import requests
import os

url = 'https://api.hydromancer.xyz/info'
headers = {
    'Authorization': f'Bearer {os.environ.get("HYDROMANCER_API_KEY")}',
    'Content-Type': 'application/json',
}
user = '0x0000000000000000000000000000000000000000'
limit = 500

params = {'type': 'userCompletedTradesByTime', 'user': user, 'startTime': 0, 'limit': limit}
while True:
    page = requests.post(url, json=params, headers=headers).json()
    if not page:
        break
    for trade in page:
        ...  # process trade
    if len(page) < limit:
        break
    last = page[-1]
    params = {
        'type': 'userCompletedTradesByTime',
        'user': user,
        'cursor': f"{last['close_time']}_{last['tx_index']}",
        'limit': limit,
    }
```

{% endtab %}

{% tab title="Javascript" %}

```javascript
import axios from 'axios';

const url = 'https://api.hydromancer.xyz/info';
const headers = {
    'Authorization': `Bearer ${process.env.HYDROMANCER_API_KEY}`,
    'Content-Type': 'application/json',
};
const user = '0x0000000000000000000000000000000000000000';
const limit = 500;

let params = { type: 'userCompletedTradesByTime', user, startTime: 0, limit };
while (true) {
    const { data: page } = await axios.post(url, params, { headers });
    if (!page.length) break;
    for (const trade of page) { /* process trade */ }
    if (page.length < limit) break;
    const last = page[page.length - 1];
    params = {
        type: 'userCompletedTradesByTime',
        user,
        cursor: `${last.close_time}_${last.tx_index}`,
        limit,
    };
}
```

{% endtab %}
{% endtabs %}

***

## Response Fields

JSON array of completed-trade records, ordered **oldest-first** by `close_time` then `tx_index`. Same record shape as [userCompletedTrades](/readme/rest-api/user-performance/usercompletedtrades.md):

| Field                  | Type    | Description                                                                                                                                                                           |
| ---------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `user`                 | string  | Ethereum address                                                                                                                                                                      |
| `coin`                 | string  | Asset symbol                                                                                                                                                                          |
| `position_type`        | string  | `"Long"` or `"Short"`. Omitted on outcome markets, which are only ever long                                                                                                           |
| `gross_pnl`            | string  | Realized PnL before fees                                                                                                                                                              |
| `net_pnl`              | string  | Realized PnL after fees and funding (`gross_pnl - fees + funding_pnl`)                                                                                                                |
| `funding_pnl`          | string  | Cumulative funding PnL accrued while the position was open (positive = received, negative = paid). Already included in `net_pnl`. Omitted on outcome markets, which accrue no funding |
| `entry_px`             | string  | Volume-weighted average entry price                                                                                                                                                   |
| `exit_px`              | string  | Volume-weighted average exit price                                                                                                                                                    |
| `position_closed_size` | string  | Size of the closed position, in contracts                                                                                                                                             |
| `fees`                 | string  | Total fees paid on fills attributed to this position                                                                                                                                  |
| `fills`                | int     | Number of fills on this position                                                                                                                                                      |
| `maker_fills`          | int     | Number of uncrossed (maker) fills on this position                                                                                                                                    |
| `open_time`            | int     | Time the position was opened (Unix ms)                                                                                                                                                |
| `close_time`           | int     | Time the position was fully closed (Unix ms) — **first half of the next-page `cursor`**                                                                                               |
| `duration_ms`          | int     | `close_time - open_time` in milliseconds                                                                                                                                              |
| `max_position_size`    | string  | Peak absolute position size during the position's lifetime                                                                                                                            |
| `builder`              | string? | Builder address from the closing fill, if any                                                                                                                                         |
| `tx_index`             | int     | Transaction index of the closing fill — **second half of the next-page `cursor`**                                                                                                     |
| `leverage`             | int     | Leverage multiplier at close (e.g. `10` = 10x). Omitted on outcome markets, which are unlevered                                                                                       |
| `is_cross`             | boolean | `true` = cross margin, `false` = isolated. Omitted on outcome markets                                                                                                                 |
