How to Track Wallet PnL on Robinhood Chain
Realized and unrealized PnL, win rate and per-token results for any Robinhood Chain wallet: the average-cost method, the API calls and code to recompute it.
Crius 8 min read
To track a wallet’s profit and loss on Robinhood Chain, call GET /v1/robinhood/wallet/{address} for a realized, unrealized and win-rate summary over 1, 7 or 30 days or all time, and /wallet/{address}/trades for every trade behind it. Crius computes PnL with the average-cost method in USD at the time of each trade, and this guide shows exactly how, so you can reproduce, audit or extend the numbers yourself.
Wallet PnL sounds simple: what you sold for minus what you paid. In practice it raises questions every PnL tool has to answer somehow. Which buy does a sell close? What price are unsold tokens worth? What about tokens that arrived by transfer, not by a trade? Here are the answers Crius uses, with code that recomputes them from raw trades.
The method: average cost, USD at trade time
For each token a wallet traded, Crius aggregates its buys and sells from the indexed trade tape (every swap on Uniswap v2, v3 and v4 and every launchpad bonding curve), attributed to the real trader rather than the router contract:
average entry = USD spent on buys / tokens boughtrealized PnL = USD received from sells − tokens sold × average entryunrealized PnL = tokens still held × current price − cost of those tokenstotal PnL = realized + unrealizedThree rules make this robust:
- USD at trade time. Every trade is valued in USD at the moment it happened, using the quote it traded against (ETH, WETH, USDG or another token) and that block’s ETH price. A token bought for ETH when ETH was $2,400 keeps that cost even if ETH moves.
- Unrealized uses a liquidity-aware price. Open positions are valued at the token’s current price. For positions too large for the pool to absorb, the value is capped at what the market could actually pay, so a wallet holding 40% of a thin token isn’t shown as a millionaire.
- Unknown cost is reported, not guessed. If a wallet sold more tokens than it ever bought, the extra arrived by transfer (an airdrop, a gift, a wallet move) and has no known cost. Crius marks the position
cost_basis: "unknown (sold tokens received by transfer)"and leaves it out of PnL rather than inventing a basis.
Average cost isn’t the only valid method. FIFO matches each sell to the oldest open buy and gives different realized numbers for positions built in steps. It is the most common one for trading analytics because it’s stable, order-independent and easy to explain. Every response states it in meta.pnl_method.
The wallet summary
curl "https://api.crius.sh/v1/robinhood/wallet/0xc56e22b48c421e08d26bad1bd0676832412a8b41?window=all&min_value_usd=1" \ -H "x-api-key: $CRIUS_API_KEY"{ "profile": { "first_seen_block": 53558073, "wallet_age_days": 50.77, "tx_count": 1043, "fresh": false, "tags": [], "funded_by": { "address": "0x776c…5ade", "amount_eth": 0.0322, "block": 53558073 }, "history": "complete" }, "pnl": { "window": "all", "realized_usd": 35104.7, "unrealized_usd": 0, "total_usd": 35104.7, "win_rate_pct": 75, "tokens_traded": 8, "tokens_with_known_cost": 8, "trades": 944, "volume_usd": 80674.3, "gas_spent_usd": 32.55, "best_trade": { "symbol": "GIGATENEV", "pnl_usd": 30132.7 }, "worst_trade": { "symbol": "CASHKITTY", "pnl_usd": -75.73 }, "avg_hold_time_s": 371340 }, "holdings": []}This is a real wallet that ranked first by PnL among one memecoin’s traders at time of writing (Oct 2026). A few fields are worth reading carefully:
windowis1d,7d,30d(default) orall. It limits which trades count, so a 7-day PnL is the result of trades made in the last 7 days, valued as described above.win_rate_pctis the share of tokens with a positive total PnL, counted only overtokens_with_known_cost. Six of eight here.gas_spent_usdcovers every transaction the wallet sent in the window, not just swaps. Subtract it for a net figure; on Robinhood Chain it’s usually small, $32.55 across 944 trades for this wallet.funded_byis the first ETH the wallet ever received, including internal transfers from contracts, which matters when you’re asking who is behind a wallet.
The wallet profile and wallet trades endpoints need a Starter key or higher.
Recomputing PnL from raw trades
The summary is convenient; the trade list is the ground truth. /wallet/{address}/trades returns every trade the wallet made across all tokens, newest first, each with its USD value at the time, gas, token tax and finality, and paginates with meta.next_cursor. This Node.js script fetches the summary, then rebuilds one token’s PnL from scratch:
// pnl.mjs: wallet PnL from the API summary and recomputed from raw trades. Node 18+.const API = "https://api.crius.sh/v1/robinhood";const KEY = process.env.CRIUS_API_KEY;const WALLET = process.argv[2];const TOKEN = process.argv[3];
const get = async (p) => { const r = await fetch(`${API}${p}`, { headers: { "x-api-key": KEY } }); const b = await r.json(); if (!r.ok) throw new Error(`${r.status} ${b.error?.code}`); return b;};
// 1. Summary over a window (1d, 7d, 30d or all).const { data: w } = await get(`/wallet/${WALLET}?window=all&min_value_usd=1`);const p = w.pnl;console.log(`realized $${p.realized_usd}, unrealized $${p.unrealized_usd}, win rate ${p.win_rate_pct}%`);console.log(`${p.tokens_traded} tokens, ${p.trades} trades, gas $${p.gas_spent_usd}`);
// 2. Every trade of one token (newest first, paginated with meta.next_cursor).const trades = [];let cursor = "";do { const q = new URLSearchParams({ token: TOKEN, limit: "200" }); if (cursor) q.set("cursor", cursor); const { data, meta } = await get(`/wallet/${WALLET}/trades?${q}`); trades.push(...data); cursor = meta.next_cursor ?? "";} while (cursor);
// 3. Average cost: realized = sold USD - tokens sold x average buy price.let bought = 0, boughtUsd = 0, sold = 0, soldUsd = 0;for (const t of trades) { if (t.type === "buy") { bought += t.amount_base; boughtUsd += t.value_usd; } else { sold += t.amount_base; soldUsd += t.value_usd; }}const avg = boughtUsd / bought;const realized = soldUsd - Math.min(sold, bought) * avg;console.log(`${trades.length} trades: bought ${bought.toFixed(0)} for $${boughtUsd.toFixed(2)}, sold for $${soldUsd.toFixed(2)}`);console.log(`average entry $${avg.toPrecision(4)}, realized PnL $${realized.toFixed(2)}`);if (sold > bought * 1.000001) console.log("sold more than bought: some tokens arrived by transfer, cost unknown");$ node pnl.mjs 0xc56e22b48c421e08d26bad1bd0676832412a8b41 0xc9675fe679c7d4e1b94416f66d61c96127eb964arealized $35104.7, unrealized $0, win rate 75%8 tokens, 944 trades, gas $32.560535 trades: bought 10997432 for $1514.14, sold for $31646.89average entry $0.0001377, realized PnL $30132.75The recomputed $30,132.75 matches the API’s best_trade to the cent. One buy of about 11 million tokens for $1,514, unwound over 34 sells for $31,647.
Per-token PnL for a whole wallet
The same idea across every token, in Python. Note that it groups by token address, not symbol:
"""pnl.py: realized PnL per token for a wallet, from its trade history (average-cost method)."""import os, sys, collections, requests
API = "https://api.crius.sh/v1/robinhood"WALLET = sys.argv[1]s = requests.Session()s.headers["x-api-key"] = os.environ["CRIUS_API_KEY"]
def trades(wallet, since_ms=None): cursor = None while True: params = {"limit": 200} if cursor: params["cursor"] = cursor if since_ms: params["from_ts"] = since_ms body = s.get(f"{API}/wallet/{wallet}/trades", params=params, timeout=60).json() yield from body["data"] cursor = body["meta"].get("next_cursor") if not cursor: return
pos = collections.defaultdict(lambda: {"sym": "", "b": 0.0, "bu": 0.0, "s": 0.0, "su": 0.0})for t in trades(WALLET): p = pos[t["token"]["address"]] p["sym"] = t["token"]["symbol"] if t["type"] == "buy": p["b"] += t["amount_base"]; p["bu"] += t["value_usd"] else: p["s"] += t["amount_base"]; p["su"] += t["value_usd"]
rows = []for token, p in pos.items(): if p["b"] == 0 or p["s"] > p["b"] * 1.000001: continue # tokens received by transfer: cost unknown avg = p["bu"] / p["b"] rows.append((p["su"] - p["s"] * avg, p["sym"], p["b"], p["s"], avg))for realized, sym, b, sold, avg in sorted(rows, reverse=True): print(f"{sym:>12} realized ${realized:>10,.2f} avg entry ${avg:.3g} sold {sold / b:.0%} of position") GIGATENEV realized $ 30,132.75 avg entry $0.000138 sold 100% of position ROO realized $ 4,608.29 avg entry $2.62e-06 sold 100% of position SI realized $ 378.65 avg entry $9.92e-05 sold 100% of position PONIE realized $ 32.76 avg entry $0.000157 sold 100% of position ROO realized $ 29.08 avg entry $2.53e-06 sold 100% of position DPONS realized $ 9.99 avg entry $1.44e-05 sold 100% of position ROO realized $ -11.11 avg entry $3.52e-06 sold 100% of position CASHKITTY realized $ -75.73 avg entry $9.38e-05 sold 100% of positionThe rows add up to the summary’s $35,104.70. The output also shows why addresses matter: this wallet traded three different tokens called ROO. Copycat tickers are everywhere on launchpad chains, and a PnL tool that groups by symbol will merge them.
PnL from the other side: a token’s traders
To rank wallets on one token, not inspect one wallet, use /token/{address}/traders. It applies the same method to every trader of the token and sorts by pnl, realized_pnl, volume, buys or roi, over 1h, 24h, 7d or all:
curl "https://api.crius.sh/v1/robinhood/token/0xc9675fe679c7d4e1b94416f66d61c96127eb964a/traders?sort=realized_pnl&window=7d&limit=10" \ -H "x-api-key: $CRIUS_API_KEY"Each row has avg_buy_usd, avg_sell_usd, roi_pct, holding_time_s and wallet tags. The smart money guide builds a cross-token leaderboard from it.
Practical notes
- Windows cut trades, not positions. A 7-day PnL ignores buys made before the window. For lifetime results per position, use
window=all. - Bots inflate trade counts. Wallets with thousands of trades a month are usually arbitrage or market-making contracts; check
is_contractand the trade count before treating a wallet as a human trader. - Multi-hop swaps are attributed to the wallet that actually received or paid the token, not the router.
viaon each trade lists the routers involved. - Taxed tokens. Each trade reports
fees.token_tax.pct. PnL uses the amounts the wallet actually received and paid, so transfer taxes are already reflected.
Frequently asked questions
Does Crius use FIFO or average cost for PnL?
Average cost, with every trade valued in USD at the time it happened. The method is stated in meta.pnl_method on trader responses. You can compute FIFO yourself from /wallet/{address}/trades, which returns every trade with its USD value.
Why is PnL missing for some tokens in a wallet?
The wallet sold more of the token than it bought, so part of its balance arrived by transfer and has no known cost. Those positions are marked cost_basis: unknown and excluded rather than guessed.
Is gas included in PnL?
PnL is gross. gas_spent_usd reports the gas of every transaction the wallet sent in the window, so subtract it for a net figure.
Can I get PnL for a past period?
Yes: window=1d, 7d, 30d or all on the wallet profile, or filter /wallet/{address}/trades with from_ts and to_ts and aggregate yourself.
How fresh is the data?
Trades appear within about a second of their block. Each trade carries finality (latest, safe or finalized) if you need settled numbers only.
Related
- Product overview: wallet analytics API, DEX trades API
- Reference: wallets, top traders, trade tape
- Guides: smart money and top traders, token holders API, WebSocket trading bot
Build with Crius
Robinhood Chain data in one API
Free plan with 1M credits a month. Your key in under a minute.
Related APIs
More from the blog
- PortfolioWallet analytics
Build a Crypto Portfolio Dashboard Using the Crius API
Build a portfolio dashboard for Robinhood Chain wallets: holdings, live valuation in one batch call, realized and unrealized PnL, gas and confidence.
- Top tradersPnL
Track Robinhood Chain Smart Money Wallets and Top Traders
Find wallets that are consistently profitable across Robinhood Chain tokens: top traders by PnL, wallet tags, and a script that filters out bots and infrastructure.
- TutorialHistorical data
How to Get All Robinhood Chain Transactions Without Running a Node
Export every trade, transfer and liquidity event for a Robinhood Chain token or wallet with an indexed API. Tested Python and Node.js, no archive node.