> ## Documentation Index
> Fetch the complete documentation index at: https://docs.clicker.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Position Calculations

> How to calculate current value, unrealized PnL, and percent change

This guide shows how to combine them to derive current value, unrealized PnL, and percent change for both spot and perp positions.

## Philosophy

Your app likely its own high-performance system for live prices. (If not, we can recommend pricing APIs for you.) For that reason, the Clicker API only returns historical data: cost basis, realized PnL, and position amounts. **You bring your own live price data to these calculations.**

These fields can be found in `metadata` and `metadata.positionStats` of a [HydratedPosition](/response-types/hydrated-position).

## Detecting Spot vs Perps

Check `metadata.perpPositionType`. If it's `"long"` or `"short"`, the position is a perp. If it's `null` or absent, it's spot.

For Hyperliquid positions, you can also check for the presence of `positionAmountWithLeverage`.

## Spot Positions

### Current Value

```
currentValue = positionAmount * currentPrice
```

| Field            | Source                    |
| ---------------- | ------------------------- |
| `positionAmount` | `metadata.positionAmount` |
| `currentPrice`   | Your pricing feed         |

### Unrealized PnL

```
costBasis = holdingsCostBasisUSD + holdingReceivedCostBasisUSD
unrealizedPnL = currentValue - costBasis
```

| Field                         | Source                                               |
| ----------------------------- | ---------------------------------------------------- |
| `holdingsCostBasisUSD`        | `metadata.positionStats.holdingsCostBasisUSD`        |
| `holdingReceivedCostBasisUSD` | `metadata.positionStats.holdingReceivedCostBasisUSD` |

`holdingsCostBasisUSD` is the cost basis of tokens the trader bought. `holdingReceivedCostBasisUSD` is the cost basis of tokens the trader received via airdrops or transfers, valued at market price at the time of receipt. Together they form the full cost basis of current holdings.

### Total PnL

To show total PnL (realized + unrealized):

```
totalPnL = realizedGainsUSD + unrealizedPnL
```

`realizedGainsUSD` captures profit or loss from tokens already sold.

### Percent Change

```
totalInvested = boughtUSD + receivedCostBasisUSD
percentChange = (totalPnL / totalInvested) * 100
```

| Field                  | Source                                        |
| ---------------------- | --------------------------------------------- |
| `boughtUSD`            | `metadata.positionStats.boughtUSD`            |
| `receivedCostBasisUSD` | `metadata.positionStats.receivedCostBasisUSD` |

Only compute percent change when `totalInvested >= 1`. Below that threshold, the percentage is meaningless.

### Example

A trader bought 10,000 tokens at \$0.05 for \$500. The token is now \$0.08.

```
currentValue   = 10000 * 0.08            = $800
costBasis      = 500 + 0                  = $500
unrealizedPnL  = 800 - 500               = $300
totalPnL       = 0 + 300                  = $300  (no realized gains yet)
percentChange  = (300 / 500) * 100        = 60%
```

## Perp Positions

Perps use leveraged values for PnL and margin (collateral) for position value.

### Unrealized PnL

Compute the current leveraged notional value:

```
leveragedNotionalValue = abs(positionAmountWithLeverage) * currentPrice
```

Then compute PnL based on direction:

```
// Long
unrealizedPnL = leveragedNotionalValue - holdingsCostBasisUSDWithLeverage

// Short
unrealizedPnL = holdingsCostBasisUSDWithLeverage - leveragedNotionalValue
```

| Field                              | Source                                                    |
| ---------------------------------- | --------------------------------------------------------- |
| `positionAmountWithLeverage`       | `metadata.positionAmountWithLeverage`                     |
| `holdingsCostBasisUSDWithLeverage` | `metadata.positionStats.holdingsCostBasisUSDWithLeverage` |

### Current Value (Equity)

For perps, the position value is the trader's equity: margin plus unrealized PnL.

```
margin = holdingsCostBasisUSD
currentValue = abs(margin + unrealizedPnL)
```

`holdingsCostBasisUSD` on a perp position represents the margin (collateral) posted, not the full notional exposure.

### Total PnL

```
totalPnL = realizedGainsUSD + unrealizedPnL
```

### Percent Change

```
totalInvested = boughtUSD + receivedCostBasisUSD
percentChange = (totalPnL / totalInvested) * 100
```

Same formula as spot. `boughtUSD` on a perp is the total margin posted across all entries.

### Example

A trader opens a 5x long on ETH at \$3,000 with \$1,000 margin. ETH is now \$3,300.

```
positionAmountWithLeverage             = 1.6667  (5000 / 3000)
holdingsCostBasisUSDWithLeverage       = $5,000
holdingsCostBasisUSD (margin)          = $1,000

leveragedNotionalValue  = 1.6667 * 3300      = $5,500
unrealizedPnL      = 5500 - 5000        = $500
currentValue       = abs(1000 + 500)     = $1,500
totalPnL           = 0 + 500            = $500
percentChange      = (500 / 1000) * 100  = 50%
```

The 10% move in ETH produced a 50% return because of 5x leverage.

## Closed Positions

A position is closed when the trader has exited entirely. For closed positions, there is no unrealized PnL to compute. Display `realizedGainsUSD` as the total PnL and compute percent change against `totalInvested` as above.

```
totalPnL      = realizedGainsUSD
percentChange = (realizedGainsUSD / totalInvested) * 100
```

You can detect a closed position by checking `metadata.positionStats.isOpen === false`, or by checking that the position's remaining value is below \$1 (the dust threshold).

## TL;DR Calculation

```typescript theme={null}
function calculatePosition(
  metadata: HydratedPosition["metadata"],
  currentPrice: number,
) {
  const { positionStats: stats } = metadata;
  const isPerp =
    metadata.perpPositionType === "long" ||
    metadata.perpPositionType === "short";

  // Unrealized PnL
  const notionalValue =
    Math.abs(
      isPerp ? metadata.positionAmountWithLeverage : metadata.positionAmount,
    ) * currentPrice;
  const costBasis = isPerp
    ? stats.holdingsCostBasisUSDWithLeverage
    : stats.holdingsCostBasisUSD + stats.holdingReceivedCostBasisUSD;
  const unrealizedPnL =
    metadata.perpPositionType === "short"
      ? costBasis - notionalValue
      : notionalValue - costBasis;

  // Current value, total PnL, percent change
  const currentValue = isPerp
    ? Math.abs(stats.holdingsCostBasisUSD + unrealizedPnL)
    : notionalValue;
  const totalPnL = stats.realizedGainsUSD + unrealizedPnL;
  const totalInvested = stats.boughtUSD + stats.receivedCostBasisUSD;
  const percentChange =
    totalInvested >= 1 ? (totalPnL / totalInvested) * 100 : undefined;

  return { currentValue, unrealizedPnL, totalPnL, percentChange };
}
```
