Check any Solana wallet's PnL — net of every fee
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, withtip_wallettelling 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.
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:
| Field | Meaning |
|---|---|
realized_pnl | SOL banked on completed round-trips — net of every fee |
unrealized | remaining token balances marked to current price (negative here: open bags under water) |
total_pnl | realized + unrealized |
fees | total fee spend across all fills — the number most checkers pretend doesn't exist |
win_rate | tokens_won / tokens_closed — 168 of 287 closed tokens ended positive |
sol_invested / sol_received / volume | gross 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.
/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.
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.