# How trader and wallet PnL is computed on Robinhood Chain

> How the Crius API computes realized and unrealized PnL, ROI, cost basis and win rate for Robinhood Chain traders and wallets, worked through on real trades.

Source: https://docs.crius.sh/concepts/pnl/ · Updated 2026-10-28

Crius computes PnL with the **average-cost method** on the trade tape: every buy and sell is valued in USD at the moment it happened, the tokens sold are charged at the average price paid for all tokens bought, and what is still held is valued at the current price. Realized PnL is what the sells made over that cost; unrealized PnL is the current value of the balance over its cost. Top traders of a token, a wallet’s profile, holder snapshots and holder cohorts all use the same rules.

## A trader’s PnL, worked through

Example [Free plan · 5 credits](https://docs.crius.sh/reference/holders/top-traders/)

### CHARGE's top three traders by PnL

Who made the most on Charge AI (0x7b8892cea949bc60d494681d1bc3ea96071ab0b8), and how is their PnL calculated?

GET/v1/robinhood/token/0x7b8892cea949bc60d494681d1bc3ea96071ab0b8/traders?limit=3

 

**cURL**

```bash
curl "https://api.crius.sh/v1/robinhood/token/0x7b8892cea949bc60d494681d1bc3ea96071ab0b8/traders?limit=3" \
  -H "x-api-key: $CRIUS_API_KEY"
```

**JavaScript**

```js
const res = await fetch("https://api.crius.sh/v1/robinhood/token/0x7b8892cea949bc60d494681d1bc3ea96071ab0b8/traders?limit=3", {
  headers: { "x-api-key": process.env.CRIUS_API_KEY },
});
const { data, meta } = await res.json();
```

**Python**

```python
import os, requests

r = requests.get(
    "https://api.crius.sh/v1/robinhood/token/0x7b8892cea949bc60d494681d1bc3ea96071ab0b8/traders?limit=3",
    headers={"x-api-key": os.environ["CRIUS_API_KEY"]},
)
body = r.json()
print(body["data"])
```

**Real response 200 Captured Oct 28, 2026 · 91 lines (arrays trimmed)**

```json
{
  "data": [
    {
      "address": "0x9e38fedc40987c19396cc20ea59eb2b1b7928881",
      "avg_buy_usd": 0.00000842823,
      "avg_sell_usd": 0.0000532304,
      "balance": 0,
      "bought": 29080767.335830362668108408,
      "bought_eth": 0.099,
      "bought_usd": 245.099,
      "buys": 1,
      "cost_basis": "complete",
      "first_trade_ts": 1791983208000,
      "gas_spent_usd": 0.0875722,
      "holding_time_s": 118676,
      "last_trade_ts": 1792101884000,
      "pnl_usd": 1302.88,
      "rank": 1,
      "realized_pnl_usd": 1302.88,
      "roi_pct": 531.573,
      "sells": 7,
      "sold": 29080767.335830362668108408,
      "sold_eth": 0.635695,
      "sold_usd": 1547.98,
      "tags": [],
      "unrealized_pnl_usd": 0
    },
    {
      "address": "0x7c79ad74d36dea424d08a5d326271bf03da81f6a",
      "avg_buy_usd": 0.00000790248,
      "avg_sell_usd": 0.0000343631,
      "balance": 0,
      "bought": 46475393.982901663958413733,
      "bought_eth": 0.1485,
      "bought_usd": 367.271,
      "buys": 2,
      "cost_basis": "complete",
      "first_trade_ts": 1791983183000,
      "gas_spent_usd": 0.0712204,
      "holding_time_s": 125872,
      "last_trade_ts": 1792109055000,
      "pnl_usd": 994.45,
      "rank": 2,
      "realized_pnl_usd": 994.45,
      "roi_pct": 270.767,
      "sells": 4,
      "sold": 37582304.39247174197236032,
      "sold_eth": 0.528147,
      "sold_usd": 1291.44,
      "tags": [],
      "unrealized_pnl_usd": 0
    },
    {
      "address": "0x83055f3e7be4e289733bbc8c7bd42a6443177f07",
      "avg_buy_usd": 0.000023621,
      "avg_sell_usd": 0.0000471805,
      "balance": 0,
      "bought": 35569497.20243776424709037,
      "bought_eth": 0.3465,
      "bought_usd": 840.189,
      "buys": 3,
      "cost_basis": "complete",
      "first_trade_ts": 1791988168000,
      "gas_spent_usd": 0.0631376,
      "holding_time_s": 4039,
      "last_trade_ts": 1791992207000,
      "pnl_usd": 838,
      "rank": 3,
      "realized_pnl_usd": 838,
      "roi_pct": 99.7395,
      "sells": 3,
      "sold": 35569497.20243776424709037,
      "sold_eth": 0.691944,
      "sold_usd": 1678.19,
      "tags": [],
      "unrealized_pnl_usd": 0
    }
  ],
  "meta": {
    "block": 100406823,
    "block_age_ms": 829,
    "block_ts": 1793225080000,
    "chain": "robinhood",
    "finalized_block": 100397649,
    "lag_ms": 64,
    "next_cursor": "3",
    "pnl_method": "average cost; USD at trade time; unrealized at current price",
    "safe_block": 100401329,
    "window": "all"
  }
}
```

What this tells you

-   Rank 1, `0x9e38fedc40987c19396cc20ea59eb2b1b7928881`: one buy of 29,080,767 CHARGE for `bought_usd: 245.099`, then seven sells of all of it for `sold_usd: 1547.98`. Realized PnL is 1,547.98 − 245.099 = `realized_pnl_usd: 1302.88`, and `roi_pct: 531.573` is 1,302.88 ÷ 245.099.
-   Rank 2, `0x7c79ad74d36dea424d08a5d326271bf03da81f6a`: bought 46,475,393 tokens for $367.271 (`avg_buy_usd: 0.00000790248`) and sold 37,582,304 of them for $1,291.44. The sold tokens cost 37,582,304 × 0.00000790248 ≈ $296.99, so `realized_pnl_usd` is 994.45.
-   Rank 2 also has `balance: 0` although it sold only part of what it bought. The other 8.9 million tokens left the wallet without a sale (a transfer), so they count neither as a sale nor as a loss.
-   `gas_spent_usd` (0.0875722 for rank 1) is reported separately and not subtracted from `pnl_usd`.
-   `cost_basis: "complete"` on all three: every token they sold was bought, so the numbers are exact.

[Top traders reference](https://docs.crius.sh/reference/holders/top-traders/) [Data looks wrong? Report it](mailto:support@crius.sh?subject=Wrong%20data%20in%20docs%20example%3A%20CHARGE's%20top%20three%20traders%20by%20PnL&body=Page%3A%20https%3A%2F%2Fdocs.crius.sh%2Fconcepts%2Fpnl%2F%23top-traders%0ARequest%3A%20GET%20%2Fv1%2Frobinhood%2Ftoken%2F0x7b8892cea949bc60d494681d1bc3ea96071ab0b8%2Ftraders%3Flimit%3D3%0A%0AWhat's%20wrong%20or%20missing%3F%0A%0A)

## The formulas

For one wallet and one token over the window (`bought` and `sold` in tokens, `bought_usd` and `sold_usd` at trade time):

| Field | How it is computed |
| --- | --- |
| `avg_buy_usd` | `bought_usd ÷ bought`: the average cost of one token |
| `realized_pnl_usd` | `sold_usd − min(sold, bought) × avg_buy_usd` |
| `unrealized_pnl_usd` | Current value of `balance` − `min(balance, bought − sold) × avg_buy_usd` |
| `pnl_usd` | `realized_pnl_usd + unrealized_pnl_usd` |
| `roi_pct` | `pnl_usd ÷ bought_usd × 100` |
| `holding_time_s` | From the first trade to now while the wallet still holds the token, otherwise from the first trade to the last |

-   **USD at trade time.** Each trade’s `value_usd` is the quote amount times the quote’s USD price at that block. ETH-quoted trades use that block’s ETH/USD (`eth_usd_at_trade` on the trade tape). Later price moves never change a past trade’s value.
-   **Current value.** The balance is valued at the token’s [current price](https://docs.crius.sh/concepts/token-prices/), capped at what the price pool’s quote side could pay out. Ten million tokens in a pool holding $50 of ETH are never worth more than $50.
-   **Windows.** `/traders` takes `window=1h`, `24h`, `7d` or `all` (the default); the wallet profile takes `1d`, `7d`, `30d` (the default) or `all`. Only trades inside the window count.

## When PnL is unknown

When a wallet **sold more than it bought**, the extra tokens came from somewhere else (a transfer, an airdrop, a launch allocation) and their cost is unknown. Crius doesn’t guess: `cost_basis` says `"unknown (sold tokens received by transfer)"`, `realized_pnl_usd`, `unrealized_pnl_usd`, `pnl_usd` and `roi_pct` are `null`, and the row sorts last when you rank by PnL.

Tokens received by transfer and **still held** are different: they are part of the balance, valued at the current price, with no cost against them, so they show up as unrealized gain. A dev wallet holding its launch allocation is the common case. Use the `dev` tag and `exclude=dev` when you rank traders (see [holder classes](https://docs.crius.sh/concepts/holder-classes/)).

## Wallet PnL

The [wallet profile](https://docs.crius.sh/reference/wallets/wallet-profile/) applies the same per-token formulas to every token the wallet traded in the window and adds them up. Tokens with an unknown cost are left out of the totals.

| `pnl` field | Meaning |
| --- | --- |
| `realized_usd`, `unrealized_usd`, `total_usd` | Sums over the tokens with a known cost |
| `win_rate_pct` | Share of those tokens with a positive total PnL (per token, not per trade) |
| `tokens_traded`, `tokens_with_known_cost` | How many tokens the wallet traded, and how many the totals cover |
| `best_trade`, `worst_trade` | The token with the highest and lowest total PnL |
| `gas_spent_usd`, `gas_scope` | Gas for all transactions the wallet sent in the window, trades or not |
| `avg_hold_time_s` | Average time from first to last trade per token |

Example [Starter plan · 5 credits](https://docs.crius.sh/reference/wallets/wallet-profile/)

### A creator wallet with no trades in 30 days

What did CHARGE's creator wallet (0x3ac82d45ff9e4963cefbc3f74dbe293f34c927ca) make or lose in the last 30 days?

GET/v1/robinhood/wallet/0x3ac82d45ff9e4963cefbc3f74dbe293f34c927ca

 

**cURL**

```bash
curl "https://api.crius.sh/v1/robinhood/wallet/0x3ac82d45ff9e4963cefbc3f74dbe293f34c927ca" \
  -H "x-api-key: $CRIUS_API_KEY"
```

**JavaScript**

```js
const res = await fetch("https://api.crius.sh/v1/robinhood/wallet/0x3ac82d45ff9e4963cefbc3f74dbe293f34c927ca", {
  headers: { "x-api-key": process.env.CRIUS_API_KEY },
});
const { data, meta } = await res.json();
```

**Python**

```python
import os, requests

r = requests.get(
    "https://api.crius.sh/v1/robinhood/wallet/0x3ac82d45ff9e4963cefbc3f74dbe293f34c927ca",
    headers={"x-api-key": os.environ["CRIUS_API_KEY"]},
)
body = r.json()
print(body["data"])
```

**Real response 200 Captured Oct 28, 2026 · 84 lines (arrays trimmed)**

```json
{
  "data": {
    "account_type": "smart_account",
    "address": "0x3ac82d45ff9e4963cefbc3f74dbe293f34c927ca",
    "delegate": "0x63c0c19a282a1b52b07dd5a65b58948a07dae32b",
    "holdings": [
      {
        "balance": 25,
        "pnl": null,
        "price_confidence": "low",
        "token": {
          "address": "0xfaaf8ffa8a29ceb6c85aff8d3961f65ae1bfc775",
          "logo": null,
          "symbol": "USDG"
        },
        "value_eth": 0.00000500064,
        "value_usd": 0.0117789
      },
      {
        "balance": 25,
        "pnl": null,
        "price_confidence": "low",
        "token": {
          "address": "0x06993ae1230d3755b54f25022028daa66772bc3d",
          "logo": null,
          "symbol": "USDG"
        },
        "value_eth": 0.00000500061,
        "value_usd": 0.0117789
      }
    ],
    "holdings_truncated": false,
    "holdings_value_usd": 0.0235578,
    "is_contract": false,
    "label": null,
    "native_balance": 0.71473787532832551,
    "native_value_usd": 1683.56,
    "pnl": {
      "avg_hold_time_s": null,
      "best_trade": null,
      "gas_scope": "all transactions sent by the wallet in the window",
      "gas_spent_usd": 0.198772,
      "realized_usd": 0,
      "tokens_traded": 0,
      "tokens_with_known_cost": 0,
      "total_usd": 0,
      "trades": 0,
      "unrealized_usd": 0,
      "volume_usd": 0,
      "win_rate_pct": null,
      "window": "30d",
      "worst_trade": null
    },
    "profile": {
      "first_seen_block": 91351250,
      "first_seen_ts": 1792283372000,
      "fresh": false,
      "funded_by": {
        "address": "0xd782268f600403843484b70710daee6846fc0db8",
        "amount_eth": 0.008148386337868892,
        "block": 87601233,
        "label": "bridge:Relay solver",
        "ts": 1791903516300
      },
      "history": "complete",
      "tags": [
        "dev",
        "smart_account"
      ],
      "tx_count": 14,
      "wallet_age_days": 10.8994
    }
  },
  "meta": {
    "balances_block": 100406864,
    "block": 100406864,
    "block_age_ms": 1301,
    "block_ts": 1793225084000,
    "chain": "robinhood",
    "finalized_block": 100397649,
    "lag_ms": 59,
    "safe_block": 100401329
  }
}
```

What this tells you

-   `pnl.window: "30d"` is the default window. `trades: 0` and `tokens_traded: 0`: the wallet made no trades in it, so `realized_usd`, `unrealized_usd` and `total_usd` are 0.
-   `win_rate_pct`, `best_trade`, `worst_trade` and `avg_hold_time_s` are `null`: with no tokens traded there is nothing to rate.
-   `gas_spent_usd: 0.198772` is still counted, because `gas_scope` is “all transactions sent by the wallet in the window”, not only trades.
-   Each holding has `pnl: null` here: neither of the two tokens it holds was traded in the window.

[Wallet profile reference](https://docs.crius.sh/reference/wallets/wallet-profile/) [Data looks wrong? Report it](mailto:support@crius.sh?subject=Wrong%20data%20in%20docs%20example%3A%20A%20creator%20wallet%20with%20no%20trades%20in%2030%20days&body=Page%3A%20https%3A%2F%2Fdocs.crius.sh%2Fconcepts%2Fpnl%2F%23wallet-pnl%0ARequest%3A%20GET%20%2Fv1%2Frobinhood%2Fwallet%2F0x3ac82d45ff9e4963cefbc3f74dbe293f34c927ca%0A%0AWhat's%20wrong%20or%20missing%3F%0A%0A)

Holder endpoints use the same cost basis. The [holder snapshot](https://docs.crius.sh/reference/holders/holder-snapshot/) adds `cost_basis_usd` and `unrealized_pnl_usd` to each holder at the snapshot’s price, and the [holder cohort](https://docs.crius.sh/reference/holders/holder-cohort/) adds `pnl_usd` to each wallet that held through the window.

## Use it in your app

1.  **Rank outside traders.** `/traders?sort=pnl&window=7d&exclude=dev,sniper` gives the best traders of the week who neither created nor sniped the token.
    
2.  **Show the basis.** Next to a PnL number, show `bought_usd`, `sold_usd` and `cost_basis`, so users can see what it rests on.
    
3.  **Subtract gas if your users care.** `pnl_usd − gas_spent_usd` is the net result after fees paid to the chain.
    

```js
// Net PnL of a token's top traders this week, outside traders only (Free plan key).
const url = `https://api.crius.sh/v1/robinhood/token/${token}/traders?sort=pnl&window=7d&exclude=dev,sniper&limit=10`;
const { data } = await (await fetch(url, { headers: { "x-api-key": process.env.CRIUS_API_KEY } })).json();

for (const t of data) {
  if (t.pnl_usd === null) continue; // cost unknown: tokens arrived by transfer
  const net = t.pnl_usd - (t.gas_spent_usd ?? 0);
  console.log(t.rank, t.address, `pnl $${t.pnl_usd}`, `net $${net.toFixed(2)}`, `roi ${t.roi_pct}%`);
}
```

## Frequently asked questions

**Does Crius PnL include gas?**

No. PnL is computed from the USD value of the trades. Gas is reported next to it as `gas_spent_usd` so you can subtract it yourself: per token on `/traders`, and for every transaction the wallet sent in the window on the wallet profile.

**Is PnL FIFO or average cost?**

Average cost. Every token bought costs the average USD price of all the wallet's buys of that token, so the order of buys and sells doesn't change the result. Each response says so in `meta.pnl_method` on `/traders`.

**Why is a trader's PnL null?**

The wallet sold more tokens than it bought: the extra tokens arrived by transfer, airdrop or launch allocation, so their cost is unknown. Those rows have `cost_basis: "unknown (sold tokens received by transfer)"` and `null` PnL, and they sort last when you rank by PnL.

**Is the wallet win rate per trade?**

Per token. `win_rate_pct` is the share of tokens with a known cost whose total PnL (realized + unrealized) is positive in the window. Ten losing trades and one big winning trade on the same token count as one win if the token's total PnL is positive.

**Which trades count toward a wallet's PnL?**

The trades attributed to it as the trader: the address whose balance changed, not the router, solver or ERC-4337 bundler that sent the transaction. See trader attribution.

## Related

[Guide **Top traders & PnL** Rank the traders of any Robinhood Chain token by PnL, volume, buys or ROI, with wallet tags, gas and hold time. A real example with the PnL math explained. Read](https://docs.crius.sh/guides/get-top-traders-pnl/) [Guide **Wallet holdings & PnL** Get a Robinhood Chain wallet's tokens with USD value, ETH balance, realized and unrealized PnL, win rate, gas spent and wallet tags in one call. Real example. Read](https://docs.crius.sh/guides/get-wallet-pnl-and-holdings/) [Guide **Diamond-hand holders** Find wallets that bought a Robinhood Chain token early and held through the run, with hold time, entry market cap, sells and PnL per wallet. Real example. Read](https://docs.crius.sh/guides/find-diamond-hand-holders/) [Concept **Trader attribution & wallet tags** How Crius attributes Robinhood Chain trades to the real trader behind routers, aggregators, bundlers and bot contracts, and what each wallet tag means. Read](https://docs.crius.sh/concepts/attribution/) [Concept **Token prices** How the Crius API prices Robinhood Chain tokens from on-chain pools, how the price pool is picked, and what confidence and thin\_liquidity mean. Read](https://docs.crius.sh/concepts/token-prices/) [API reference **Top traders API** Traders of the token ranked by PnL, realized PnL, volume, buys or ROI over a window (1h, 24h, 7d, all), with wallet tags: dev, sniper, whale, smart\_account (EIP-7702 wallets), the app for app wallets Read](https://docs.crius.sh/reference/holders/top-traders/)
