Overview

The Provider API makes it easy to aggregate positions across multiple providers and verticals. This recipe shows how to build a portfolio dashboard that tracks your positions on Hyperliquid, Aster, and Polymarket in one view.

Architecture

Step 1: Fetch Positions from All Providers

const BASE = "https://api.perps.studio/v1";
const WALLET = "0xYourWalletAddress";
const headers = { "X-API-Key": API_KEY };

async function fetchAllPositions() {
  const [hlPerps, asterPerps, hlSpot, predictions] = await Promise.all([
    // Hyperliquid perps positions
    fetch(`${BASE}/perps/hyperliquid/account/${WALLET}/positions`, { headers })
      .then((r) => r.json())
      .catch(() => []),

    // Aster perps positions
    fetch(`${BASE}/perps/aster/account/${WALLET}/positions`, { headers })
      .then((r) => r.json())
      .catch(() => []),

    // Hyperliquid spot balances
    fetch(`${BASE}/spot/hyperliquid/balances/${WALLET}`, { headers })
      .then((r) => r.json())
      .catch(() => []),

    // Polymarket prediction positions
    fetch(`${BASE}/predictions/polymarket/positions/${WALLET}`, { headers })
      .then((r) => r.json())
      .catch(() => []),
  ]);

  return {
    perps: [
      ...hlPerps.map((p) => ({ ...p, provider: "hyperliquid" })),
      ...asterPerps.map((p) => ({ ...p, provider: "aster" })),
    ],
    spot: hlSpot.map((b) => ({ ...b, provider: "hyperliquid" })),
    predictions: predictions.map((p) => ({ ...p, provider: "polymarket" })),
  };
}

Step 2: Aggregate PnL

function calculatePortfolioSummary(positions) {
  const summary = {
    totalEquity: 0,
    totalUnrealizedPnl: 0,
    perpsValue: 0,
    spotValue: 0,
    predictionsValue: 0,
    positions: [] as any[],
  };

  // Aggregate perps PnL
  for (const pos of positions.perps) {
    const pnl = parseFloat(pos.unrealizedPnl ?? "0");
    const value = parseFloat(pos.size) * parseFloat(pos.markPrice);
    summary.totalUnrealizedPnl += pnl;
    summary.perpsValue += value;
    summary.positions.push({
      type: "perps",
      provider: pos.provider,
      symbol: pos.symbol,
      side: pos.side,
      size: pos.size,
      pnl: pnl.toFixed(2),
    });
  }

  // Aggregate spot value
  for (const bal of positions.spot) {
    const value = parseFloat(bal.total);
    summary.spotValue += value;
  }

  // Aggregate predictions value
  for (const pred of positions.predictions) {
    const value = parseFloat(pred.size) * parseFloat(pred.currentPrice);
    summary.predictionsValue += value;
    summary.positions.push({
      type: "prediction",
      provider: pred.provider,
      market: pred.marketId,
      outcome: pred.outcome,
      size: pred.size,
      currentPrice: pred.currentPrice,
    });
  }

  summary.totalEquity =
    summary.perpsValue + summary.spotValue + summary.predictionsValue;

  return summary;
}

Step 3: Poll on Interval

async function runTracker() {
  console.log("Portfolio Tracker started\n");

  setInterval(async () => {
    const positions = await fetchAllPositions();
    const summary = calculatePortfolioSummary(positions);

    console.clear();
    console.log("=== Portfolio Summary ===");
    console.log(`Total Equity:      $${summary.totalEquity.toFixed(2)}`);
    console.log(`Unrealized PnL:    $${summary.totalUnrealizedPnl.toFixed(2)}`);
    console.log(`Perps Value:       $${summary.perpsValue.toFixed(2)}`);
    console.log(`Spot Value:        $${summary.spotValue.toFixed(2)}`);
    console.log(`Predictions Value: $${summary.predictionsValue.toFixed(2)}`);
    console.log("\n=== Open Positions ===");

    for (const pos of summary.positions) {
      console.log(
        `  [${pos.provider}] ${pos.symbol ?? pos.market} | ${pos.side ?? pos.outcome} | PnL: $${pos.pnl ?? "N/A"}`
      );
    }
  }, 10000); // Refresh every 10s
}

runTracker();

Enhancements

Store snapshots of your portfolio value over time using the fills endpoint. Calculate realized PnL from fill history.
Set up alerts when unrealized PnL exceeds a threshold or when a position’s liquidation price is within a certain percentage of the mark price.
Build a React/Next.js dashboard that visualizes your portfolio with charts. Use WebSocket feeds for real-time price updates instead of REST polling.