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

# Pricing Engine Deep Dive

> How Mobula calculates accurate token prices using weighted averages and outlier filtering

# Pricing Engine: Weighted Averages & Outlier Filtering

Mobula's pricing engine aggregates price data from hundreds of liquidity pools across multiple chains to compute a single, reliable USD price for every token. This guide explains the mathematics and algorithms behind accurate price discovery.

<Info>
  **Why it matters**: A token like WETH might trade at slightly different prices on Uniswap, Sushiswap, Curve, and 50 other DEXs. Our engine combines all these prices into one reliable value, filtering out manipulated pools and scams.
</Info>

## Overview: The Pricing Pipeline

```
Pool Data Collection → Mode Selection → Outlier Filtering → Weight Calculation → Weighted Average
```

Each step is critical for accuracy:

1. **Pool Data Collection**: Gather price, volume, reserve, and depth from all pools
2. **Mode Selection**: Choose between volume-based or reserve-based weighting
3. **Outlier Filtering**: Remove scam pools and manipulated prices
4. **Weight Calculation**: Apply stable multiplier and depth adjustments
5. **Weighted Average**: Compute final price

***

## Step 1: Pool Data Collection

For each pool trading a token, we extract:

```typescript theme={null}
interface PoolWeight {
  price: number;           // Token's USD price in this pool
  normalizedVolume: number; // 24h volume (0 if below threshold)
  realVolume: number;      // Actual 24h volume for metrics
  reserve: number;         // Total liquidity (TVL)
  depthUp: number;         // Buy-side market depth
  depthDown: number;       // Sell-side market depth
}
```

**Example**: Token XYZ trading on 4 pools:

| Pool        | Price  | Volume | Reserve | Paired With |
| ----------- | ------ | ------ | ------- | ----------- |
| Uniswap V3  | \$1.00 | \$5M   | \$10M   | USDC        |
| Sushiswap   | \$1.02 | \$2M   | \$4M    | ETH         |
| PancakeSwap | \$0.98 | \$1M   | \$2M    | USDT        |
| ScamDEX     | \$0.10 | \$1K   | \$10K   | ETH         |

***

## Step 2: Mode Selection

The engine chooses a weighting strategy based on available data:

```typescript theme={null}
const computeMode: 'volume' | 'reserve' = 
  totalNormalizedVolume > 0 ? 'volume' : 'reserve';
```

| Condition                 | Mode      | Weight Source    | Use Case            |
| ------------------------- | --------- | ---------------- | ------------------- |
| Any pool has volume > 0   | `volume`  | normalizedVolume | Active tokens       |
| All pools have volume = 0 | `reserve` | reserve (TVL)    | New/inactive tokens |

**Reserve mode** is a fallback for newly listed or low-activity tokens where volume data isn't reliable.

***

## Step 3: Outlier Filtering (Two-Phase Median)

This is the most critical step. It removes:

* Scam pools with manipulated prices
* Stale pools with outdated prices
* Low-liquidity pools with extreme slippage

### Phase 1: Weighted Median Calculation

```typescript theme={null}
// Sort prices by value
const sortedPrices = prices.sort((a, b) => a - b);

// Find weighted median
let accumulatedWeight = 0;
let medianPrice = 0;

for (let i = 0; i < sortedPrices.length; i++) {
  accumulatedWeight += weights[i];
  if (accumulatedWeight >= totalWeight / 2) {
    medianPrice = sortedPrices[i];
    break;
  }
}
```

### Phase 2: Log-Space Deviation Check

```typescript theme={null}
// Calculate deviation in log space (handles multiplicative differences)
const logPrice = Math.log(price);
const logMedian = Math.log(medianPrice);
const deviation = Math.abs(logPrice - logMedian);

// Threshold: 10% in volume mode, 15% in reserve mode (more permissive)
const threshold = computeMode === 'reserve' 
  ? BASE_THRESHOLD * 1.5 
  : BASE_THRESHOLD;

const isOutlier = deviation > threshold;
```

**Why log space?**

* A price of $0.50 vs $1.00 (50% lower) should be treated the same as $2.00 vs $1.00 (100% higher)
* Log space makes these symmetric: `log(0.5) = -0.69`, `log(2) = 0.69`

### Filtering Result

After filtering our example:

| Pool        | Price  | Status                    |
| ----------- | ------ | ------------------------- |
| Uniswap V3  | \$1.00 | ✅ Valid (near median)     |
| Sushiswap   | \$1.02 | ✅ Valid (2% deviation)    |
| PancakeSwap | \$0.98 | ✅ Valid (2% deviation)    |
| ScamDEX     | \$0.10 | ❌ Outlier (90% deviation) |

***

## Step 4: Weight Calculation

### Stable Multiplier

Pools paired with stablecoins provide more reliable pricing than pools paired with volatile assets:

```typescript theme={null}
const STABLE_MULTIPLIER = 3;

let weight = normalizedVolume;

// Non-stable pairs get boosted weight to compensate for their
// typically lower volume but higher price accuracy
if (!baseQuote.isStableCoin({ chainId, address })) {
  weight *= STABLE_MULTIPLIER;
}
```

**Why boost non-stable pairs?**

* Stable-paired pools (USDC, USDT) give direct USD prices
* ETH-paired pools require ETH→USD conversion, introducing small errors
* But ETH pairs often have MORE volume, so we boost stable pairs to balance

### Depth Weighting (Optional)

When `ponderWithDepth = true`:

```typescript theme={null}
if (ponderWithDepth) {
  const depth = depthUp + depthDown;
  weight = depth > 0 ? normalizedVolume * depth : normalizedVolume;
}
```

This prioritizes pools where large trades can execute with minimal slippage.

### Final Weights Example

| Pool        | Volume | Paired | Multiplier | Final Weight |
| ----------- | ------ | ------ | ---------- | ------------ |
| Uniswap V3  | \$5M   | USDC   | ×1         | \$5M         |
| Sushiswap   | \$2M   | ETH    | ×3         | \$6M         |
| PancakeSwap | \$1M   | USDT   | ×1         | \$1M         |

***

## Step 5: Weighted Average

The final price is computed as:

```typescript theme={null}
function computeWeightedMean(
  prices: number[], 
  weights: number[], 
  count: number
): [number, number] {
  let weightedSum = 0;
  let totalWeight = 0;
  
  for (let i = 0; i < count; i++) {
    weightedSum += prices[i] * weights[i];
    totalWeight += weights[i];
  }
  
  const price = weightedSum / totalWeight;
  return [price, totalWeight];
}
```

### Example Calculation

```
numerator = (1.00 × 5M) + (1.02 × 6M) + (0.98 × 1M)
          = 5M + 6.12M + 0.98M
          = 12.1M

denominator = 5M + 6M + 1M = 12M

finalPrice = 12.1M / 12M = $1.0083
```

***

## Asset-Level Pricing

For tokens deployed on multiple chains, we aggregate token prices into a single **asset price**.

### Why Aggregate?

The same token can have different prices on different chains due to:

* Bridge delays and costs
* Chain-specific liquidity
* Arbitrage opportunities

### Aggregation Algorithm

```typescript theme={null}
computeAssetPrice(tokenResults: Map<SelectorStr, TokenResult>) {
  const count = tokenResults.size;
  if (count === 0) return null;
  
  // Determine weighting mode
  let totalVolume = 0;
  let totalReserve = 0;
  for (const data of tokenResults.values()) {
    totalVolume += data.volume;
    totalReserve += data.reserve;
  }
  
  const useVolume = totalVolume > 0;
  const total = useVolume ? totalVolume : totalReserve;
  
  // Filter by threshold (1% minimum share)
  const VOLUME_THRESHOLD = 0.01;
  const validTokens = [];
  
  for (const data of tokenResults.values()) {
    const share = useVolume 
      ? data.volume / total 
      : data.reserve / total;
    
    if (share >= VOLUME_THRESHOLD) {
      validTokens.push(data);
    }
  }
  
  // Weighted average
  return computeWeightedMean(
    validTokens.map(t => t.price),
    validTokens.map(t => useVolume ? t.volume : t.reserve),
    validTokens.length
  );
}
```

### Example: USDT Asset Price

| Chain         | Token Price | Volume | Share               |
| ------------- | ----------- | ------ | ------------------- |
| Ethereum      | \$1.001     | \$500M | 89.3%               |
| BSC           | \$0.999     | \$50M  | 8.9%                |
| Polygon       | \$1.000     | \$10M  | 1.8%                |
| Obscure Chain | \$0.50      | \$100  | 0.00002% ← Filtered |

```
assetPrice = (1.001×500M + 0.999×50M + 1.000×10M) / 560M
           = $1.0008
```

***

## Outlier Detection for Assets

When aggregating across chains, we also filter token-level outliers:

```typescript theme={null}
const ASSET_TOKEN_OUTLIER_THRESHOLD = 2.0; // 200% deviation

for (const token of filteredTokens) {
  const priceDeviation = Math.abs(token.price - assetPrice) / assetPrice;
  
  if (priceDeviation > ASSET_TOKEN_OUTLIER_THRESHOLD) {
    outlierTokens.push(token);
    continue;
  }
  validTokens.push(token);
}

// Re-compute price without outliers
if (outlierTokens.length > 0 && validTokens.length > 0) {
  return computeWeightedMean(validTokens);
}
```

***

## Performance Optimizations

### Pre-allocated Buffers

```typescript theme={null}
// Avoid allocations in hot path
private readonly priceBuffer: Float64Array;
private readonly weightBuffer: Float64Array;
private readonly logPriceBuffer: Float64Array;

constructor(maxPoolsPerToken = 256) {
  this.priceBuffer = new Float64Array(maxPoolsPerToken);
  this.weightBuffer = new Float64Array(maxPoolsPerToken);
  this.logPriceBuffer = new Float64Array(maxPoolsPerToken);
}
```

### Selector Caching

```typescript theme={null}
private readonly selectorCache: Map<SelectorStr, { chainId: ChainId; address: string }>;

private parseSelectorCached(selector: SelectorStr) {
  let cached = this.selectorCache.get(selector);
  if (!cached) {
    cached = parseSelector(selector);
    if (this.selectorCache.size < 1000) {
      this.selectorCache.set(selector, cached);
    }
  }
  return cached;
}
```

***

## Validation & Safety

### Price Bounds

```typescript theme={null}
const INSANE_PRICE_THRESHOLD = 1e15; // $1 quadrillion

isValidPrice(price: number): boolean {
  return price > 0 && 
         price < INSANE_PRICE_THRESHOLD && 
         Number.isFinite(price);
}
```

### Minimum Valid Prices

```typescript theme={null}
const MIN_VALID_PRICES = 2;

// Require at least 2 valid pools for reliable pricing
if (validIndices.length < MIN_VALID_PRICES) {
  return null;
}
```

***

## API Access

### Token Price

```bash theme={null}
# Get price for Fartcoin on Solana
curl -X GET "https://demo-api.mobula.io/api/2/token/details?blockchain=solana&address=9BB6NFEcjBCtnNLFko2FqVQBq8HHM13kCyYcdQbgpump" \
  -H "Authorization: YOUR_API_KEY"

# Get price for Wojak on Solana
curl -X GET "https://demo-api.mobula.io/api/2/token/details?blockchain=solana&address=8J69rbLTzWWgUJziFY8jeu5tDwEPBwUz4pKBMr5rpump" \
  -H "Authorization: YOUR_API_KEY"

# Get WETH price on Ethereum
curl -X GET "https://demo-api.mobula.io/api/2/token/details?blockchain=ethereum&address=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" \
  -H "Authorization: YOUR_API_KEY"
```

Response includes:

```json theme={null}
{
  "data": {
    "priceUSD": 0.0234,
    "volume24hUSD": 890000.50,
    "liquidityUSD": 250000.75,
    "priceChange24hPercentage": -5.8,
    "approximateReserveUSD": 125000.50
  }
}
```

### Real-Time Updates

For real-time price updates, use the [WSS Token Details](/indexing-stream/stream/websocket/wss-token-details) WebSocket stream.

***

## Summary

| Step               | Purpose                     | Key Technique               |
| ------------------ | --------------------------- | --------------------------- |
| Data Collection    | Gather all pool data        | Multi-DEX aggregation       |
| Mode Selection     | Choose weighting strategy   | Volume vs Reserve fallback  |
| Outlier Filtering  | Remove manipulated prices   | Two-phase log-space median  |
| Weight Calculation | Prioritize reliable sources | Stable multiplier (×3)      |
| Weighted Average   | Compute final price         | Σ(price × weight) / Σweight |

The result: **accurate, manipulation-resistant prices** updated in real-time across 100+ blockchains.
