Check any Solana wallet's PnL — net of every fee

· 8 min read

wallet pnlwin ratefeespositionscopy trading

Every PnL tracker will tell you a wallet is up. Very few will tell you what it cost to be up. On pump.fun the costs are everywhere: a protocol fee on every fill, a creator fee, an LP fee once the token trades on PumpSwap, a priority fee to get ordered inside the block, and a validator tip to get in at all. For a high-frequency sniper those line items routinely eat the visible edge — a wallet that looks comfortably green on gross fills can be barely break-even net. This guide builds a real Solana wallet PnL checker on the Raiden API, where every number is net of every fee.

Why most PnL numbers lie by omission

The naive computation is SOL out minus SOL in per token. That misses five separate costs, and each one is a real field on the fills returned by /wallets/{addr}/swaps:

  • fee — the pump.fun protocol fee on the trade.
  • creator_fee — the token creator's cut.
  • lp_fee — the liquidity-pool fee, once the token trades on PumpSwap.
  • priority_fee — the in-protocol compute-budget spend (cu_price, in micro-lamports per CU, × cu_limit) that orders the transaction inside the block.
  • tip — the out-of-band validator tip, with tip_wallet telling you which submission provider was paid.

A sniper paying a 0.002 SOL tip plus priority fees on every attempt — including the attempts that landed and reverted, where the fees are paid and nothing is bought — bleeds SOL that never shows up in a gross calculation. Every PnL figure in the Raiden API is reported after deducting all of them. When a wallet reads +171 SOL here, that is SOL that actually accumulated in the wallet.

Convention: all SOL amounts are strings in lamports (1 SOL = 1,000,000,000 lamports). Strings, because a 64-bit JSON float silently loses precision on large balances. Convert with int(value) / 1e9.

Check wallet profit on Solana in one call: /stats

curl -H "X-API-Key: $RAIDEN_KEY" \
  "https://terminal.raiden.wtf/api/wallets/8psN…VRtf/stats"
{
  "trader": "8psN…VRtf",
  "realized_pnl": "184500000000",
  "unrealized": "-12750000000",
  "total_pnl": "171750000000",
  "sol_invested": "642300000000",
  "sol_received": "826800000000",
  "volume": "1469100000000",
  "fees": "3820000000",
  "tokens_traded": 312,
  "tokens_closed": 287,
  "tokens_won": 168,
  "win_rate": 0.5853658536585366,
  "trades": 1944,
  "launch_buys": 41,
  "avg_hold_sec": 1837.42,
  "sold_gt_bought": 153,
  "never_sold": 25,
  "fast_flip": 22,
  "dist":    { "d500": 31, "d200": 54, "d0": 83, "dneg": 97, "drug": 22 },
  "mc_dist": { "lo": 198, "mid": 89, "hi": 25, "buys": 312 },
  "first_seen": "2026-05-02T08:22:51Z",
  "last_seen": "2026-07-21T11:03:18Z",
  "flags": ["dev", "sniper", "profitable", "whale"],
  "created": [ … ]
}

The headline block:

FieldMeaning
realized_pnlSOL banked on completed round-trips — net of every fee
unrealizedremaining token balances marked to current price (negative here: open bags under water)
total_pnlrealized + unrealized
feestotal fee spend across all fills — the number most checkers pretend doesn't exist
win_ratetokens_won / tokens_closed — 168 of 287 closed tokens ended positive
sol_invested / sol_received / volumegross flows, for sizing the operation

Note what wallet win rate on Solana means here: only closed positions count. An open bag down 80% doesn't dent the win rate until it's sold — it sits in unrealized instead. A high win rate next to a deeply negative unrealized and a fat never_sold count is a wallet that banks its winners and refuses to realize its losers. The two numbers only tell the truth together.

Coverage: the index keeps a rolling six-month window and has a complete record of every pump.fun trade since May 1, 2026. "All-time" in /stats means all-time within that record.

Distributions: how the profit was actually made

Two wallets with identical total PnL can be completely different animals, and the two distribution objects separate them. dist partitions the closed tokens by realized outcome: d500 closed above +500% and d200 the +200–500% band — the big multiples — d0 everything from break-even to +200%, dneg losses down to −50%, and drug anything worse: the rug-shaped exits. A wallet whose profit lives in d500 is a lottery-ticket hunter; one grinding it out in d0 is a scalper whose edge depends on fees staying low.

mc_dist does the same for entry market cap: of all buys, how many entered low (lo), mid (mid) or high (hi) — 198/89/25 here says this wallet is overwhelmingly an early buyer. The behavior fields round out the profile: avg_hold_sec, launch_buys (entries at launch — sniper behavior), fast_flip (quick in-and-out), sold_gt_bought (tokens where it sold more than it bought — inventory arriving from other wallets), and the flags array (dev, sniper, profitable, whale). For scoring long lists there's also /wallets/{addr}/stats-lite — a fast, positions-only subset of /stats.

Is this wallet hot now? /window-stats

All-time stats have a survivorship problem: a wallet can coast for months on one legendary week. /wallets/{addr}/window-stats?w=1d|7d|30d returns the same shape as /stats computed only over the recent window:

curl -H "X-API-Key: $RAIDEN_KEY" \
  "https://terminal.raiden.wtf/api/wallets/8psN…VRtf/window-stats?w=7d"
{
  "trader": "8psN…VRtf",
  "realized_pnl": "18450000000",
  "unrealized": "3200000000",
  "total_pnl": "21650000000",
  "fees": "488888889",
  "tokens_traded": 47,
  "tokens_closed": 39,
  "tokens_won": 24,
  "win_rate": 0.6153846153846154,
  "trades": 213,
  "dist":    { "d500": 5, "d200": 9, "d0": 10, "dneg": 11, "drug": 4 },
  "mc_dist": { "lo": 18, "mid": 21, "hi": 8, "buys": 47 },
  "flags": ["profitable", "whale"],
  …
}

+21.65 SOL net and a 61% win rate over the last 7 days: this wallet is not a museum piece, it's currently printing. Comparing w=1d, 7d and 30d against all-time is the fastest tell for a cooling streak — exactly what you want before copying anyone, as covered in how to track pump.fun wallets.

Per-token breakdown: /positions

The totals say whether; positions say where. Sortable by sort=last|pnl|invested|balance|trades|first with dir and offset:

curl -H "X-API-Key: $RAIDEN_KEY" \
  "https://terminal.raiden.wtf/api/wallets/8psN…VRtf/positions?sort=pnl&dir=desc&limit=50"
{
  "data": [
    {
      "trader": "8psN…VRtf",
      "mint": "9xQm…pump",
      "symbol": "PEPE",
      "token_balance": "488888888888888",
      "sol_invested": "2500000000",
      "sol_received": "1800000000",
      "tokens_bought": "732000000000000",
      "tokens_sold": "243111111111112",
      "realized_pnl": "-700000000",
      "avg_cost": "0.0000034153",
      "current_price": "0.00000042",
      "first_trade_at": "2026-06-21T09:41:14Z",
      "last_trade_at": "2026-06-24T17:08:53Z",
      "trade_count": 14
    }
  ]
}

Each row is one wallet-token pair: invested vs received, tokens bought vs sold, the remaining token_balance, avg_cost against current_price. This row is a live loss: −0.7 SOL realized and a big bag bought 8× above the current price. For a single token, /wallets/{addr}/positions/{mint} returns the same shape directly (404 if the wallet never touched it) — the fastest way to answer "what did this wallet make on that launch?".

Charting the equity: /pnl-curve and /flow-candles

Two endpoints feed charts directly. /wallets/{addr}/pnl-curve?range=1d|7d|30d|all returns per-bucket net SOL flow in lamports — cumulatively summed, it's the equity curve:

{ "data": [ { "t": 1718960400, "net": "-488888889" }, … ] }

/wallets/{addr}/flow-candles?tf=1h&w=7d aggregates realized PnL as OHLC candles (tf=swap|5m|15m|1h|4h|1d, w=1d|7d|30d) — drop it into any candlestick component and a wallet's week reads like a price chart, drawdowns included:

{ "data": [ { "t": 1718960400, "o": "488888889", "h": "1250000000", "l": "312500000", "c": "973400000" }, … ] }

Rank a list of wallets by 7-day net PnL and win rate

The Solana wallet PnL API pattern you'll actually reuse: loop a candidate list through window-stats, convert lamport strings, filter thin samples, rank.

import requests

BASE = "https://terminal.raiden.wtf/api"
H = {"X-API-Key": "YOUR_KEY"}
LAMPORTS = 1e9

wallets = ["8psN…VRtf", "Cb3f…g9rE", "5Q5q…Funr"]  # your candidate list

rows = []
for w in wallets:
    s = requests.get(f"{BASE}/wallets/{w}/window-stats",
                     params={"w": "7d"}, headers=H).json()
    rows.append({
        "wallet":   w,
        "net_pnl":  int(s["total_pnl"]) / LAMPORTS,   # net of every fee
        "realized": int(s["realized_pnl"]) / LAMPORTS,
        "fees":     int(s["fees"]) / LAMPORTS,
        "win_rate": s["win_rate"],
        "closed":   s["tokens_closed"],
    })

# skip thin samples, rank by 7d net PnL with win rate as tiebreaker
rows = [r for r in rows if r["closed"] >= 10]
rows.sort(key=lambda r: (r["net_pnl"], r["win_rate"]), reverse=True)

for r in rows:
    print(f"{r['wallet'][:6]}  7d net {r['net_pnl']:+8.2f} SOL  "
          f"win {r['win_rate']:.0%} on {r['closed']} closed  "
          f"fees {r['fees']:.2f} SOL")

Watch the fees column against net_pnl: a wallet netting +5 SOL on 3 SOL of fees is one fee-market spike away from negative. That fragility is invisible in every checker that reports gross numbers.

PnL is half the picture

A profitable wallet is not automatically a wallet you want to follow — some of the most profitable addresses on pump.fun earn their PnL inside coordinated rugs. Cross every candidate with /wallets/{addr}/dumps: the full paginated history of rugged tokens the wallet took part in, one row per token with the same_slot flag (dumped in the dev's exact slot — coordination, not luck), its as_operator / as_sniper role and the SOL extracted, plus a first-page summary of total_dumps, same_slot count and total sol_extracted (this endpoint reports SOL, not lamports). Filter with since, min_sol and same_slot=1. A 62% win rate with 595 same-slot dumps isn't skill — it's a payroll entry.

From here: build your candidate lists with wallet tracking, judge the execution half of the edge with the wallet-vs-wallet landing comparison, and find every field of every endpoint in the API reference. The profile above — stats, windows, positions, curves — is a handful of calls against the Raiden pump.fun API, and none of the numbers will flatter anyone.

FAQ

Frequently asked questions

What fees does the PnL account for?

All of them: the pump.fun protocol fee, the creator fee, the LP fee on PumpSwap fills, the priority fee and validator tips. The API breaks them out as separate fields on the fills it returns (fee, creator_fee, lp_fee, priority_fee, tip), and every PnL figure — realized, unrealized, total — is reported after deducting them.

How is a Solana wallet's win rate calculated?

win_rate = tokens_won / tokens_closed. Only closed positions count: an open bag that is down 80% does not hurt the win rate until it is closed. That is why win rate should always be read next to never_sold and the unrealized field.

What is the difference between realized and unrealized PnL?

Realized PnL is SOL actually banked from completed round-trips, net of every fee. Unrealized marks the remaining token balances to the current price. total_pnl is the sum of the two. A wallet with a large positive unrealized on illiquid tokens may never manage to bank it.

Why are amounts returned as strings instead of numbers?

All SOL amounts are strings in lamports (1 SOL = 1,000,000,000 lamports). Serializing as strings avoids the silent precision loss a 64-bit JSON float would introduce on large balances. Convert with int(value) / 1e9.

How do I check whether a wallet is profitable right now rather than all-time?

Call /wallets/{addr}/window-stats with w=1d, 7d or 30d. It returns the same shape as /stats but computed only over that window — a wallet living off one big win two months ago shows up flat or negative on w=7d.

How much history does the checker cover?

A rolling six-month window, with a complete record of every pump.fun trade since May 1, 2026. All-time fields in /stats mean all-time within that record.

Build on the same data

Private beta — free invite-only keys, full REST + WebSocket access, and the same firehose that powers the Terminal.