Skip to content

How PnL is Calculated

FinCobra uses a per-token cost basis model. For each token you hold, we track the cost of your current position using a zero-crossing algorithm, then compute PnL as currentValue - costBasis.

Sources without transaction history (Solana, Hyperliquid, Aster) show balance only — no PnL.


Per-Token PnL — Zero-Crossing Cost Basis Method

For sources with transaction history (Binance, Bybit, BTC xpub, and EVM wallets), FinCobra calculates PnL for each individual token. This uses a zero-crossing algorithm that isolates the cost basis of your current position.

Key Insight

Only the current position matters. If you held 1 BTC, sold it all, then bought 0.5 BTC — the cost basis only tracks the 0.5 BTC purchase. Everything before the balance hit zero is irrelevant, because that was a different position.

How it Works

The algorithm has two phases:

Phase 1 — Find the zero crossing (walk backward)

Starting from your current balance, walk backward through transaction history, undoing each transaction's effect:

  1. Begin with runningBalance = currentBalance
  2. For each transaction (newest to oldest), subtract the transaction's delta
  3. When runningBalance reaches 0 or below — that's the zero crossing. This is when your current position was opened.

Phase 2 — Accumulate cost basis (walk forward)

Starting from the zero crossing, walk forward through transactions:

TransactionCost Basis Effect
Deposit / BuycostBasis += usdValue
Withdrawal / SellcostBasis *= (1 - amountSold / totalHeld) — proportional reduction
Earn subscribe / redeemNo effect (internal move between spot and earn)

Finally: PnL = currentValue - costBasis

Worked Example

You trade ETH on Binance. Your current balance is 2 ETH at $3,000 each ($6,000 total). Here's your transaction history:

#DateTypeAmountUSD ValueRunning Balance
1Jan 1Buy+3 ETH$6,0003 ETH
2Jan 15Sell-3 ETH$6,6000 ETH
3Feb 1Buy+1.5 ETH$4,2001.5 ETH
4Feb 10Sell-0.5 ETH$1,5001 ETH
5Mar 1Buy+1 ETH$3,2002 ETH

Phase 1 — Walk backward from 2 ETH:

StepTransactionDeltaRunning Balance
Start2.0
Undo #5Buy +1 ETH-11.0
Undo #4Sell -0.5 ETH+0.51.5
Undo #3Buy +1.5 ETH-1.50.0

Zero crossing found at transaction #3. Everything before (transactions #1 and #2) is a previous, closed position.

Phase 2 — Walk forward from #3:

StepTransactionCost BasisTotal Held
#3Buy 1.5 ETH @ $4,200$4,2001.5
#4Sell 0.5 ETH @ $1,500$4,200 * (1 - 0.5/1.5) = $2,8001.0
#5Buy 1 ETH @ $3,200$2,800 + $3,200 = $6,0002.0

Result:

costBasis = $6,000
currentValue = 2 ETH * $3,000 = $6,000
PnL = $6,000 - $6,000 = $0 (0%)

Even though the account saw profitable trades, the current position's cost basis matches its value — $0 PnL on this position.


Portfolio PnL

Portfolio PnL is computed client-side by aggregating per-token PnL across all wallets and exchanges:

portfolioPnl = Σ(liveTokenValue - costBasis) for all tokens with PnL data
portfolioPercent = portfolioPnl / totalCostBasis * 100

Sources without per-token PnL data (Solana, Hyperliquid, Aster) contribute to total portfolio value but not to PnL.


Reliability Flag

Per-token PnL rows have a reliable flag. PnL is marked as unreliable when:

  • No zero crossing found — the position predates the available transaction history, so cost basis may be incomplete
  • Missing USD values — any deposit or buy transaction in the cost basis path has a NULL or zero USD value

Unreliable PnL values are shown with an asterisk (*) in the dashboard. They represent a best-effort estimate rather than a precise calculation.

Wallet Per-Token PnL

EVM wallets (Ethereum, Arbitrum, BSC) and BTC xpub wallets support per-token PnL. The algorithm is the same zero-crossing method, with wallet-specific transaction type mapping:

Wallet tx typeDelta
receive+amount (like a deposit)
send-amount (like a withdrawal)
contract_interactiondirection based on sign of amount
approval0 (no balance change)

Wallet token PnL is triggered when the frontend sends token balances to the backend during each balance refresh cycle.

Wrapped Token Normalization

Wrapped native tokens are automatically normalized: WBNB balances are treated as BNB, WETH as ETH. This ensures that PnL calculations correctly merge native and wrapped positions.

BSC Native Asset

BSC chain native transactions are correctly stored as BNB (not ETH). The CHAIN_NATIVE_ASSET constant maps each chain's sourceId to its native token symbol.

Tracked Assets

Per-token PnL is computed for the following priceable assets:

BTC, ETH, SOL, BNB, XRP, DOGE, ADA, AVAX, DOT, LINK, MATIC, UNI, ATOM, NEAR, TRX, TON, HBAR, PEPE, SHIB, ARB, LTC, SUI, APT, OP, AAVE, FIL

Other tokens (random ERC20s, unlisted tokens) are excluded from per-token PnL but still count toward the account's total USD value.

Dust Threshold

Token positions with a USD value below $1 are excluded from per-token PnL calculations. This prevents noise from trivially small balances (airdrops, dust from trades, etc.).

Stablecoins

The following stablecoins are excluded from per-token PnL since their value is pegged to $1:

USDT, USDC, BUSD, FDUSD, TUSD, DAI, USDP

Stablecoin balances still count toward your account's total USD value.