---
title: "Closed-position stats for a wallet"
method: GET
path: "/api/public/v1/pulse/trader/{address}/closed-positions/stats"
tags: ["Pulse"]
---

# Closed-position stats for a wallet

`GET /api/public/v1/pulse/trader/{address}/closed-positions/stats`

## Request

- Base URL: `https://api.coinversa.ai`
- URL: `https://api.coinversa.ai/api/public/v1/pulse/trader/{address}/closed-positions/stats`
- Auth: API key in header `X-API-Key`
- TypeScript: <https://skmtc.dev/coinversa/apis/coinversa-api/docs/api/public/v1/pulse/trader/:address/closed-positions/stats/get.ts> (a function with typed inputs; its response is checked against the schema)

Aggregate over the wallet's CLOSED position lifecycles: count, average/min/max hold time, total and average realized PnL, win/loss counts, win rate, and the first/last close time (RFC 3339). Source: the latest 10,000 eligible rows for the wallet in position_lifecycles_full — closed perps only, excluding censored lifecycles that opened before the table's start. There is no time cutoff. The 10,000-position cap bounds work for very active wallets; totalPositions is therefore at most 10,000. losingPositions counts realized_pnl <= 0 (flat closes included), so winningPositions + losingPositions = totalPositions. winRate = winningPositions / totalPositions x 100. Coverage: this route previously aggregated the legacy closed_positions table, whose history reaches further back than the lifecycle table (which begins on 2026-08-31) but whose PnL was the unrealized snapshot at close rather than realized PnL, and whose exact per-position resolution could not complete within the request budget for wallets with tens of thousands of closed positions. totalPositions and firstClose can therefore be lower/later than before because lifecycle history is shorter and the newest 10,000-position cap may apply; PnL figures are now realized. Wallets with no eligible closed lifecycle return zero counts and omit firstClose/lastClose.

## Path parameters

- `address` string, required: Wallet address to inspect (0x followed by 40 hexadecimal characters; case-insensitive). An address with no indexed history returns an empty result, not 404.

## Response `200`

OK

- ClosedPositionStatsBody
  - `$schema` string, uri: A URL to the JSON Schema for this object.
  - `avgDurationMs` number, double, required
  - `avgPnl` number, double, required
  - `firstClose` string
  - `lastClose` string
  - `losingPositions` integer, required
  - `maxDurationMs` number, double
  - `minDurationMs` number, double
  - `totalPnl` number, double, required
  - `totalPositions` integer, required
  - `winRate` number, double, required
  - `winningPositions` integer, required

## Other responses

- `400`: Bad Request
- `403`: Forbidden
- `404`: Not Found
- `422`: Unprocessable Entity
- `429`: Too Many Requests
- `500`: Internal Server Error
- `504`: Gateway Timeout

## Changes

- **2026-09-23** `f2eba29805e5`: 9 info
  - api tag `Pulse` added
  - added the non-success response with the status `403`
  - removed the non-success response with the status `401`
  - added the optional property `reason` to the response with the `400` status
  - …5 more

[Operation history](https://skmtc.dev/coinversa/apis/coinversa-api/changes/api/public/v1/pulse/trader/:address/closed-positions/stats/get.md)

---

[API](https://skmtc.dev/coinversa/apis/coinversa-api.md) - [All operations](https://skmtc.dev/coinversa/apis/coinversa-api/llms.txt) - [OpenAPI document](https://skmtc.dev/coinversa/apis/coinversa-api/revisions/3454317a5830?raw)
