> ## Documentation Index
> Fetch the complete documentation index at: https://docs.polynode.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Copy PnL

> Estimate copying slippage with date filters, optional full history, and lifetime realized-PnL context.

Calculate a wallet's gross trading cash flow and the cash flow of a simulated copier using Polynode's indexed fills and settlement events. This replaces the deprecated [V2 on-demand endpoint](/api-reference/backtesting/copy-pnl).

## Request

```bash theme={null}
curl -H "x-api-key: pn_live_YOUR_KEY" \
  "https://api.polynode.dev/v3/wallets/0xab03f377164c3726d498e56f7a9398cd8eb74494/copy-pnl"
```

| Parameter    | Location | Default  | Description                                                                                                                                               |
| ------------ | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `address`    | Path     | Required | `0x` followed by 40 hexadecimal characters. Normalized to lowercase.                                                                                      |
| `max_trades` | Query    | `50000`  | Integer from `1` to `50000`. Maximum number of recent wallet-owned order fills to include.                                                                |
| `history`    | Query    | `recent` | `recent`, `auto`, or `full`. In `auto`, `max_trades` is the fallback cap; in `full`, it is the initial scan size, not a limit on the returned fill count. |
| `period`     | Query    | None     | `7d`, `14d`, `30d`, `60d`, `90d`, or `180d`, ending at `to` or the calculation snapshot.                                                                  |
| `from`       | Query    | None     | Inclusive start, as Unix seconds or `YYYY-MM-DD` at UTC midnight. Overrides `period`.                                                                     |
| `to`         | Query    | Snapshot | Exclusive end, in the same formats. `from` must be earlier than `to`.                                                                                     |

The cap counts **fills**, not positions, tokens, transactions, or distinct orders. The calculation selects the wallet's own signed-order fill records, including its taker-order execution record. It avoids counting that wallet again as the counterparty to other matched orders. This attribution is different from requesting both address roles on the raw trades endpoint.

There is no default date window. With `history=recent`, active wallets can reach 50,000 fills quickly; quieter wallets can cover years. Date filters apply before the fill cap, including to settlement events. A wallet with fewer matching fills returns all matching indexed fills. `coverage.history_truncated` tells you whether older fills **inside the requested date window** were excluded by the fill selection.

## History modes

* `recent`: return up to `max_trades` recent fills. This preserves the original V3 default.
* `auto`: first complete the recent calculation, then spend up to two additional seconds extending toward all matching history, within the overall calculation deadline. Return full history if it completes; otherwise return the original completed recent calculation with an explicit fallback reason.
* `full`: require all matching indexed history. If the work or time limit is reached, return an error instead of a recent-window fallback.

Full-history work is bounded at 500,000 fills and 100,000 settlement events per wallet. Both `auto` and `full` can return more than 50,000 fills. `max_trades` remains limited to 50,000 and controls the initial recent calculation. A successful `full` result contains all matching history, never the first 500,000 fills of a larger history.

For example, request a complete historical month with `?history=full&from=2026-08-01&to=2026-09-01`, or use `?history=auto&period=90d` to allow an explicit recent-window fallback.

Read `coverage.requested_history`, `served_history` (`full` or `recent`), and `fallback_reason` on every result. `served_history=full` means the requested date window is complete; `lifetime_history_included` is true only when no date filters or fill truncation restrict the indexed history. Automatic fallback reasons are `query_timeout`, `query_work_limit`, `activity_limit_exceeded`, or `history_limit_exceeded`. If the initial recent calculation fails, there is no valid fallback and the request returns an error.

## Calculation

* Buy: the copier pays `min(price × 1.02, 1.00) × shares`.
* Sell: the copier receives `price × 0.98 × shares`.
* Splits consume collateral; merges and redemptions release collateral. Neg-risk conversions include released collateral according to their selected outcomes. These settlement flows receive no simulated slippage.

```text theme={null}
actual_pnl_usdc = sells - buys + settlement_in - settlement_out
backtest_copy_pnl_usdc = actual_pnl_usdc - slippage_amount_usdc
slippage_cost_rate_pct = slippage_amount_usdc / abs(actual_pnl_usdc) × 100
toxic_for_copying = slippage_cost_rate_pct > 15
```

The 2% assumption is relative to each execution price. It is a fixed screening model, not a measured execution cost or a forecast. When the absolute gross cash flow is below 1 USDC, both the percentage and `toxic_for_copying` are `null`.

<Warning>
  `pnl_definition` is `cashflow`. Fees are excluded, opening inventory is not valued, and open positions are not marked to market. Sales or redemptions of inventory acquired before the selected window can therefore make this differ substantially from realized or total portfolio PnL. Deposits, withdrawals, and token transfers are outside this calculation. It covers spot order fills, not combo or perpetual trading.
</Warning>

If older matching fills are excluded, settlement inclusion begins at the exact block and log of the first included fill, inclusive. Otherwise, all indexed settlement history within the requested dates is eligible, including events before the first fill. A wallet with settlement activity and no fills can still return a cash-flow result. Explicit timestamps are checked exactly; the database snapshot bounds which events are visible.

## Realized-PnL context

`realized_pnl_context` provides the wallet's stored lifetime realized profit and position counts alongside the cash-flow simulation. Its `total_realized_pnl_usdc` is actual realized profit from the wallet summary; it is not the copier's simulated realized profit and is not used in the slippage or copying flag formulas.

This context always has `scope: "lifetime_wallet"` and `date_filters_applied: false`. It is **not rewound by `from` or `to`** and is not a realized-profit result for a historical backtest window. Read `updated_at`, `updated_block`, and `refresh_age_seconds` to see the summary's last update. `latest_indexed_fill_block` supports the `freshness` indicator: `behind_indexed_fills`, `no_newer_indexed_fills`, `unknown`, or `unavailable`. No newer fills does not prove all settlement or valuation inputs are current.

If no summary is available, `available` is false and its profit/count/update fields are null, rather than zero. `position_count` and `open_positions` describe the stored lifetime summary. They do not reproduce V2's count of positions with nonzero realized PnL.

## Response

The following is an illustrative two-fill calculation: a buy costing 5 USDC followed by a sale returning 8 USDC, with no settlement flows.

```json theme={null}
{
  "data": {
    "wallet": "0x1111111111111111111111111111111111111111",
    "actual_pnl_usdc": "3.000000",
    "backtest_copy_pnl_usdc": "2.740000",
    "slippage_amount_usdc": "0.260000",
    "slippage_cost_rate_pct": "8.666667",
    "toxic_for_copying": false,
    "trade_count": 2,
    "pnl_definition": "cashflow",
    "fees_included": false,
    "opening_inventory_valued": false,
    "open_positions_marked_to_market": false,
    "slippage_bps": {"buy": 200, "sell": 200, "buy_price_capped_at": "1.000000"},
    "cashflows": {
      "buy_cost_usdc": "5.000000",
      "sell_revenue_usdc": "8.000000",
      "settlement_in_usdc": "0.000000",
      "settlement_out_usdc": "0.000000"
    },
    "event_counts": {"buys": 1, "sells": 1, "splits": 0, "merges": 0, "redemptions": 0, "neg_risk_conversions": 0},
    "coverage": {
      "selection": "latest_wallet_order_fills",
      "max_trades": 50000,
      "history_truncated": false,
      "requested_history": "recent",
      "served_history": "full",
      "fallback_reason": null,
      "lifetime_history_included": true,
      "window_start": {"type": "beginning_of_indexed_history"},
      "window_end": {"type": "database_snapshot", "snapshot_at": "2026-09-07T01:00:00.000Z"},
      "first_included_event_timestamp": 1788652800,
      "last_included_event_timestamp": 1788739200,
      "snapshot_at": "2026-09-07T01:00:00.000Z"
    },
    "applied_filters": {"period": null, "from": null, "to": 1788742800},
    "realized_pnl_context": {
      "available": true,
      "scope": "lifetime_wallet",
      "date_filters_applied": false,
      "source": "api.wallet_pnl_current",
      "pnl_definition": "realized",
      "total_realized_pnl_usdc": "3.000000",
      "position_count": 1,
      "open_positions": 0,
      "updated_block": "93350000",
      "updated_at": "2026-09-07T00:59:00Z",
      "refresh_age_seconds": 60,
      "latest_indexed_fill_block": "93350000",
      "freshness": "no_newer_indexed_fills"
    },
    "query_ms": 800
  },
  "cache": {"status": "miss", "age_ms": 800},
  "elapsed_ms": 805
}
```

Money and percentages are decimal strings with six fractional digits. Event timestamps are Unix seconds. `snapshot_at` is a UTC timestamp. For a truncated history, `window_start` contains `type: "inclusive_trade_cursor"`, `block_number` and `log_index` as strings, and `timestamp` in Unix seconds. A complete window with a start date uses `type: "inclusive_timestamp"` and `timestamp`. `window_end` is either an `exclusive_timestamp` or a `database_snapshot`. `applied_filters` reports the resolved date bounds; without explicit dates, its `to` records the snapshot time in whole seconds. An empty wallet has zero cash-flow amounts and `null` first/last event timestamps.

Results may be cached for 30 seconds after calculation, separately for each wallet and set of options. `cache.status` is `miss`, `hit`, or `coalesced` (shared in-progress work). `cache.age_ms` measures time since the calculation's snapshot; `data.query_ms` describes that calculation, including any attempted history expansion, while `elapsed_ms` describes the current request. Each wallet has its own snapshot, including in a batch. Relative periods are anchored to that calculation; supply explicit `from` and `to` to fix the same date window across wallets.

## Access and errors

Available on paid V3 plans under the account's normal [REST limits](/guides/rate-limits) and monthly usage policy. The V2-specific one-request-per-five-seconds limit does **not** apply. Each HTTP request counts as one API request, including a batch. Cache hits still count as requests. Database capacity is bounded; account request limits are not a guarantee of uncached calculation throughput.

| HTTP status | Error code                                                                                                                                                     | Meaning                                                                                                  |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `400`       | `invalid_wallet`, `invalid_max_trades`, `invalid_history`, `invalid_period`, `invalid_timestamp`, `invalid_window`, `duplicate_parameter`, `unknown_parameter` | Invalid input. Trade-detail flags remain unsupported.                                                    |
| `422`       | `activity_limit_exceeded`                                                                                                                                      | More than 100,000 settlement events fall in the selected window. No partial financial total is returned. |
| `422`       | `unsupported_accounting_data`, `query_work_limit`                                                                                                              | Unsupported accounting inputs or calculation work limit reached. No partial total is returned.           |
| `422`       | `history_limit_exceeded`                                                                                                                                       | More than 500,000 fills are required for full history. No partial total is returned.                     |
| `503`       | `server_busy`, `database_unavailable`                                                                                                                          | Capacity or data access is temporarily unavailable.                                                      |
| `504`       | `query_timeout`                                                                                                                                                | The calculation or queue deadline was exceeded.                                                          |

Authentication, plan access, monthly usage, and account rate-limit failures use the normal V3 edge responses. Do not treat an error as zero PnL.

## Migrating from V2

Use `GET /v3/wallets/{address}/copy-pnl` or [V3 Copy PnL Batch](/data/wallets/copy-pnl-batch). Read the summary from `data`; batch input uses `wallets` instead of `addresses`. `period`, `from`, and `to` are supported. V2 defaulted to 30 days; send `period=30d` explicitly to request that window in V3. Use `history=full` when the whole requested window is required, or `auto` when an explicit recent-window fallback is acceptable.

Read actual lifetime realized PnL from `realized_pnl_context.total_realized_pnl_usdc`, with the scope and freshness limitations above. This does not restore V2's window-specific weighted-average position metrics. `include_trades`, individual trade arrays, weighted entry/holding metrics, and V2's BYOB tracked-wallet pool, snapshot, and leaderboard remain outside these endpoints.


## OpenAPI

````yaml GET /v3/wallets/{address}/copy-pnl
openapi: 3.1.0
info:
  title: PolyNode API
  description: >-
    Real-time Polymarket data API with decoded mempool settlements, OHLCV
    candles, and full Polygon JSON-RPC proxy.
  contact:
    name: PolyNode
    url: https://polynode.dev
  license:
    name: ''
  version: 2.0.0
servers:
  - url: https://api.polynode.dev
    description: Production
security:
  - api_key: []
paths:
  /v3/wallets/{address}/copy-pnl:
    get:
      tags:
        - V3 Copy PnL
      summary: Copy PnL
      description: >-
        Gross spot trading cash flow and fixed 2% copying-slippage simulation,
        with exact date filters, recent/automatic/strict full-history modes, and
        separately scoped lifetime realized-PnL context. Default recent cap
        50000; full-history work limit 500000 fills. Normal paid V3 limits
        apply. Fees and inventory valuation are excluded from the cash-flow
        simulation.
      operationId: getV3CopyPnl
      parameters:
        - name: address
          in: path
          required: true
          schema:
            type: string
            pattern: ^0x[0-9a-fA-F]{40}$
        - name: max_trades
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50000
            default: 50000
          description: >-
            Recent/fallback fill cap per wallet; initial scan size in full mode.
            Auto/full may return more fills, up to the 500000-fill work limit.
        - name: history
          in: query
          schema:
            type: string
            enum:
              - recent
              - auto
              - full
            default: recent
          description: >-
            recent keeps the cap; auto attempts full history with an explicit
            recent fallback; full requires complete matching history or returns
            an error.
        - name: period
          in: query
          schema:
            type: string
            enum:
              - 7d
              - 14d
              - 30d
              - 60d
              - 90d
              - 180d
          description: >-
            Optional window ending at to or the calculation snapshot. No default
            period. Explicit from overrides period.
        - name: from
          in: query
          schema:
            oneOf:
              - type: integer
                minimum: 0
                maximum: 253402300799
              - type: string
                pattern: ^(\d+|\d{4}-\d{2}-\d{2})$
          description: >-
            Inclusive start: Unix seconds or YYYY-MM-DD at UTC midnight. Applies
            to fills and settlement events, not lifetime realized-PnL context.
        - name: to
          in: query
          schema:
            oneOf:
              - type: integer
                minimum: 0
                maximum: 253402300799
              - type: string
                pattern: ^(\d+|\d{4}-\d{2}-\d{2})$
          description: >-
            Exclusive end: Unix seconds or YYYY-MM-DD at UTC midnight. Defaults
            to the calculation snapshot.
      responses:
        '200':
          description: Complete bounded wallet calculation
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - cache
                  - elapsed_ms
                properties:
                  data:
                    $ref: '#/components/schemas/CopyPnlSummary'
                  cache:
                    $ref: '#/components/schemas/CopyPnlCache'
                  elapsed_ms:
                    type: integer
              example:
                data:
                  wallet: '0x1111111111111111111111111111111111111111'
                  actual_pnl_usdc: '3.000000'
                  backtest_copy_pnl_usdc: '2.740000'
                  slippage_amount_usdc: '0.260000'
                  slippage_cost_rate_pct: '8.666667'
                  toxic_for_copying: false
                  trade_count: 2
                  pnl_definition: cashflow
                  fees_included: false
                  opening_inventory_valued: false
                  open_positions_marked_to_market: false
                  slippage_bps:
                    buy: 200
                    sell: 200
                    buy_price_capped_at: '1.000000'
                  cashflows:
                    buy_cost_usdc: '5.000000'
                    sell_revenue_usdc: '8.000000'
                    settlement_in_usdc: '0.000000'
                    settlement_out_usdc: '0.000000'
                  event_counts:
                    buys: 1
                    sells: 1
                    splits: 0
                    merges: 0
                    redemptions: 0
                    neg_risk_conversions: 0
                  coverage:
                    selection: latest_wallet_order_fills
                    max_trades: 50000
                    history_truncated: false
                    requested_history: recent
                    served_history: full
                    fallback_reason: null
                    lifetime_history_included: true
                    window_start:
                      type: beginning_of_indexed_history
                    window_end:
                      type: database_snapshot
                      snapshot_at: '2026-09-07T01:00:00.000Z'
                    first_included_event_timestamp: 1788652800
                    last_included_event_timestamp: 1788739200
                    snapshot_at: '2026-09-07T01:00:00.000Z'
                  applied_filters:
                    period: null
                    from: null
                    to: 1788742800
                  realized_pnl_context:
                    available: true
                    scope: lifetime_wallet
                    date_filters_applied: false
                    source: api.wallet_pnl_current
                    pnl_definition: realized
                    total_realized_pnl_usdc: '3.000000'
                    position_count: 1
                    open_positions: 0
                    updated_block: '93350000'
                    updated_at: '2026-09-07T00:59:00Z'
                    refresh_age_seconds: 60
                    latest_indexed_fill_block: '93350000'
                    freshness: no_newer_indexed_fills
                  query_ms: 800
                cache:
                  status: miss
                  age_ms: 800
                elapsed_ms: 805
        '400':
          description: Invalid input
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    $ref: '#/components/schemas/CopyPnlError'
                required:
                  - error
        '422':
          description: Accounting or work limit; no partial total
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    $ref: '#/components/schemas/CopyPnlError'
                required:
                  - error
        '503':
          description: Temporarily unavailable or busy
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    $ref: '#/components/schemas/CopyPnlError'
                required:
                  - error
        '504':
          description: Calculation or queue deadline exceeded
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    $ref: '#/components/schemas/CopyPnlError'
                required:
                  - error
components:
  schemas:
    CopyPnlSummary:
      type: object
      properties:
        wallet:
          type: string
        actual_pnl_usdc:
          type: string
        backtest_copy_pnl_usdc:
          type: string
        slippage_amount_usdc:
          type: string
        slippage_cost_rate_pct:
          type:
            - string
            - 'null'
        toxic_for_copying:
          type:
            - boolean
            - 'null'
        trade_count:
          type: integer
          minimum: 0
          maximum: 500000
        pnl_definition:
          type: string
        fees_included:
          type: boolean
        opening_inventory_valued:
          type: boolean
        open_positions_marked_to_market:
          type: boolean
        slippage_bps:
          type: object
          properties:
            buy:
              type: integer
            sell:
              type: integer
            buy_price_capped_at:
              type: string
          required:
            - buy
            - sell
            - buy_price_capped_at
        cashflows:
          type: object
          properties:
            buy_cost_usdc:
              type: string
            sell_revenue_usdc:
              type: string
            settlement_in_usdc:
              type: string
            settlement_out_usdc:
              type: string
          required:
            - buy_cost_usdc
            - sell_revenue_usdc
            - settlement_in_usdc
            - settlement_out_usdc
        event_counts:
          type: object
          properties:
            buys:
              type: integer
            sells:
              type: integer
            splits:
              type: integer
            merges:
              type: integer
            redemptions:
              type: integer
            neg_risk_conversions:
              type: integer
          required:
            - buys
            - sells
            - splits
            - merges
            - redemptions
            - neg_risk_conversions
        coverage:
          type: object
          properties:
            selection:
              type: string
            max_trades:
              type: integer
              minimum: 1
              maximum: 50000
            history_truncated:
              type: boolean
            window_start:
              oneOf:
                - type: object
                  properties:
                    type:
                      const: beginning_of_indexed_history
                  required:
                    - type
                - type: object
                  properties:
                    type:
                      const: inclusive_trade_cursor
                    block_number:
                      type: string
                    log_index:
                      type: string
                    timestamp:
                      type: number
                  required:
                    - type
                    - block_number
                    - log_index
                    - timestamp
                - type: object
                  properties:
                    type:
                      const: inclusive_timestamp
                    timestamp:
                      type: integer
                  required:
                    - type
                    - timestamp
            first_included_event_timestamp:
              type:
                - number
                - 'null'
            last_included_event_timestamp:
              type:
                - number
                - 'null'
            snapshot_at:
              type: string
              format: date-time
            requested_history:
              type: string
              enum:
                - recent
                - auto
                - full
            served_history:
              type: string
              enum:
                - recent
                - full
              description: >-
                Full means all indexed events matching the requested dates, not
                necessarily lifetime history.
            fallback_reason:
              type:
                - string
                - 'null'
              enum:
                - null
                - query_timeout
                - query_work_limit
                - activity_limit_exceeded
                - history_limit_exceeded
            lifetime_history_included:
              type: boolean
            window_end:
              oneOf:
                - type: object
                  properties:
                    type:
                      const: exclusive_timestamp
                    timestamp:
                      type: integer
                  required:
                    - type
                    - timestamp
                - type: object
                  properties:
                    type:
                      const: database_snapshot
                    snapshot_at:
                      type: string
                      format: date-time
                  required:
                    - type
                    - snapshot_at
          required:
            - selection
            - max_trades
            - history_truncated
            - window_start
            - first_included_event_timestamp
            - last_included_event_timestamp
            - snapshot_at
            - requested_history
            - served_history
            - fallback_reason
            - lifetime_history_included
            - window_end
        query_ms:
          type: integer
        realized_pnl_context:
          type: object
          properties:
            available:
              type: boolean
            scope:
              const: lifetime_wallet
            date_filters_applied:
              const: false
            source:
              const: api.wallet_pnl_current
            pnl_definition:
              const: realized
            total_realized_pnl_usdc:
              type:
                - string
                - 'null'
              description: >-
                Actual lifetime realized profit as stored at the recorded
                update; not filtered by from/to and not used in the slippage
                score.
            position_count:
              type:
                - integer
                - 'null'
            open_positions:
              type:
                - integer
                - 'null'
            updated_block:
              type:
                - string
                - 'null'
            updated_at:
              type:
                - string
                - 'null'
              format: date-time
            refresh_age_seconds:
              type:
                - integer
                - 'null'
              minimum: 0
            latest_indexed_fill_block:
              type:
                - string
                - 'null'
            freshness:
              type: string
              enum:
                - behind_indexed_fills
                - no_newer_indexed_fills
                - unknown
                - unavailable
              description: >-
                Compares summary update block with latest indexed fill. Does not
                certify settlement/valuation freshness.
          required:
            - available
            - scope
            - date_filters_applied
            - source
            - pnl_definition
            - total_realized_pnl_usdc
            - position_count
            - open_positions
            - updated_block
            - updated_at
            - refresh_age_seconds
            - latest_indexed_fill_block
            - freshness
        applied_filters:
          type: object
          properties:
            period:
              type:
                - string
                - 'null'
            from:
              type:
                - integer
                - 'null'
            to:
              type: integer
          required:
            - period
            - from
            - to
      required:
        - wallet
        - actual_pnl_usdc
        - backtest_copy_pnl_usdc
        - slippage_amount_usdc
        - slippage_cost_rate_pct
        - toxic_for_copying
        - trade_count
        - pnl_definition
        - fees_included
        - opening_inventory_valued
        - open_positions_marked_to_market
        - slippage_bps
        - cashflows
        - event_counts
        - coverage
        - query_ms
        - realized_pnl_context
        - applied_filters
    CopyPnlCache:
      type: object
      required:
        - status
        - age_ms
      properties:
        status:
          type: string
          enum:
            - hit
            - miss
            - coalesced
        age_ms:
          type: integer
          minimum: 0
    CopyPnlError:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
        message:
          type: string
  securitySchemes:
    api_key:
      type: apiKey
      in: header
      name: x-api-key

````