# Polymarket wallet P\&L including losing positions (`omraydev/polymarket-wallet-pnl`) Actor

Reconstruct a Polymarket wallet's public trades, losing positions, realized and unrealized P\&L, win rate by price bucket and monthly results. The official profile hides losing positions; this actor reconciles trades against resolutions so they appear.

- **URL**: https://apify.com/omraydev/polymarket-wallet-pnl.md
- **Developed by:** [Om Ray](https://apify.com/omraydev) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $250.00 / 1,000 wallet reports

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Polymarket wallet P\&L

Before you copy a Polymarket wallet, you probably want to know how its losing bets affect the record you're looking at. This actor reads the wallet's public trades and matches them to confirmed market results, keeping positions that settled at zero in the calculation. It also accounts for sales before settlement, so the report doesn't assume every purchase was held until the market resolved.

The report covers the trades Polymarket makes available, which may be only a recent part of an active wallet's history. The public API stops pagination at offset 10,000, and the actor can request the final page for a maximum of 10,500 raw rows before removing duplicates. Check the report's date range and coverage flags before comparing wallets, because older purchases or losses may be missing. Past performance is not a prediction of future returns or a recommendation to copy anyone's trades.

### Run the actor

Enter an exact public username or the wallet's proxy address in the `wallet` field. You don't need a Polymarket account or a wallet key, and the actor never places orders. A username must resolve to one exact public profile, so use the proxy address if the name is ambiguous.

```json
{ "wallet": "Theo4", "maxTrades": 10500 }
```

The default `maxTrades` value requests every available page through the public offset cap. You can lower it for a smaller sample, but doing so can remove the purchases needed to calculate a later sale's cost. Requests use `takerOnly=false` to include publicly exposed maker and taker trades.

For a local run, install the repository dependencies with `npm ci` at the root, then run the commands below. You don't need an Apify login to run it locally. The actor writes the full report to `storage/key_value_stores/default/OUTPUT.json` and the dataset records to `storage/datasets/default/`.

```sh
cd actors/polymarket-wallet-pnl
npx apify run --input-file examples/input.json
```

### Read the report

The report includes the fetched trades and reconstructed positions alongside realized and unrealized P\&L. You can compare win rates by average purchase-price bucket or market category, and inspect monthly results alongside official public profile data, all-time leaderboard P\&L and current position value. Missing official data appears as `null`, with the request error retained in the report. The dataset separates `trade`, `position` and `report` records so you can export the underlying trades without putting the whole wallet into one large row.

For example, ten shares bought at $0.80 and held until they resolve to zero produce the result below. That's a worked example rather than a real wallet. For complete reports, look at the real public-API runs in the repository's `samples/` directory.

```json
{
  "status": "resolved",
  "winner": false,
  "reconstructedShares": 10,
  "purchaseCost": 8,
  "realizedPnl": -8,
  "unrealizedPnl": 0,
  "totalPnl": -8
}
```

### How the calculation works

A sale releases the average purchase cost of the shares sold. Any shares remaining at a confirmed resolution receive a value of $1 for the winning token or $0 for the losing token, even if the wallet has since redeemed them. A closed market without exactly one confirmed winner stays unresolved. The resulting share count describes inventory reconstructed from trades, not the wallet's current balance.

For open inventory, the actor uses the current position mark when available and otherwise uses the CLOB token price. That mark is a valuation, and there may be no bid at that price. `summary.knownPnl` adds only the usable, priced parts of the report, while `summary.reconstructedGrossPnl` is `null` if the history is truncated, a position lacks a usable cost basis or mark, or any trade is invalid. Even when that total is available, it covers only the fetched trades and excludes fees and rewards.

Win rate measures how many completed token positions have positive P\&L among those with a usable cost basis. Sold positions count as completed, and breakeven positions remain in the denominator. Price buckets use the average observed purchase price, while categories come from CLOB metadata and tags and remain `unknown` when absent.

Monthly sale results use the month of execution. Settlement results use Gamma's `closedTime`, which is a market close time rather than a cash redemption date. If that timestamp is missing, the result stays in `unknown-resolution-month`; the actor never substitutes the scheduled `endDate`.

### What the public history cannot tell you

A position marked `costBasisComplete` has internally consistent observed fills, which doesn't prove that its lifetime cost basis is known. `lifetimeCostBasisVerified` and `accountPnlComplete` are always false, because public trades can't prove either one. Truncated positions carry `historyMayBeMissing`, and positions touching the earliest retained timestamp carry `touchesHistoryBoundary`. The report also exposes `coverage.truncated`, `stopReason`, page records and the retained date range so you can see where collection stopped.

The public trade feed doesn't reconstruct transfers, deposits or other non-trade changes such as splits, merges, redemptions, fees and rewards. An unmatched sale excludes that token position from P\&L, as does a mismatch with current official inventory for an unresolved position. Current positions without observed trades appear separately. When the feed runs out, that only shows Polymarket stopped returning history. A capped history can also miss older losses even when the visible positions reconcile.

Polymarket returns the newest fills first, so the actor reverses that order before accounting for trades with the same timestamp. Mixed buy-and-sell ties carry `sameSecondOrderingAssumed` because the timestamps can't prove execution order. Pages can change during collection, and the feed has no unique fill ID, so deduplication uses the transaction, wallet, token, timestamp, side, size and price together. Two real fills with identical values can therefore merge into one. API failures and missing market metadata show up in `errors`.

Official P\&L and this gross reconstruction use different coverage and accounting, so subtracting one from the other doesn't measure hidden losses. Check the coverage notes to see whether the observed trades are enough for the question you're asking. What you get is public data and arithmetic on it, not an audited account balance or a trading signal.

### Charges and verification

Each completed report costs one `wallet-report` event, and trade or position rows aren't charged on their own. On a paid run the actor checks your budget before it calls the API, then writes and charges the summary row before it releases the full JSON and raw trades. If the charge fails you don't get those outputs, and if the charge goes through but writing the full output fails, the summary row stays in the dataset. The repository's `PUBLISH.md` documents the event pricing and Console setup.

Run `npm test` from the repository root to check the accounting and collection code against canned public-API responses. The tests cover losing settlements and sales as well as missing inventory, open marks, monthly attribution and pagination limits. The actor attributes each sale at average cost and never counts hypothetical copy trades as wallet P\&L.

# Actor input Schema

## `wallet` (type: `string`):

Public proxy wallet address or exact Polymarket username. Example: Theo4. Ambiguous names fail rather than select a different person.

## `maxTrades` (type: `integer`):

Maximum raw rows to fetch. Default 10500 attempts all pages through the public offset cap of 10000. Smaller values are useful for a sample, but truncate history and can remove cost basis.

## Actor input object example

```json
{
  "wallet": "Theo4",
  "maxTrades": 500
}
```

# Actor output Schema

## `report` (type: `string`):

No description

## `dataset` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "wallet": "Theo4",
    "maxTrades": 500
};

// Run the Actor and wait for it to finish
const run = await client.actor("omraydev/polymarket-wallet-pnl").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "wallet": "Theo4",
    "maxTrades": 500,
}

# Run the Actor and wait for it to finish
run = client.actor("omraydev/polymarket-wallet-pnl").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "wallet": "Theo4",
  "maxTrades": 500
}' |
apify call omraydev/polymarket-wallet-pnl --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,omraydev/polymarket-wallet-pnl"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/5Zg82lDOeskqO0mJb/builds/FVaokoFTElilEdjx8/openapi.json
