Skip to main content
Back to Projects
Screenshot of Lighter OI Aggregator

Lighter OI Aggregator

Open sourceBackend service

Background service for Lighter.xyz that computes total open interest across every perpetuals market. A worker takes a WebSocket snapshot every 15 minutes, normalises it to the two-sided figure Lighter shows in its own app and stores it in Upstash Redis; a stateless Express API serves it to dashboards with a single read.

snapshot refresh cycle
15 min
one-sided feed normalised to the two-sided total
× 2
endpoint: GET /api/open-interest
1

Tech stack

  • Node.js
  • WebSockets (ws)
  • Upstash Redis
  • Express
  • REST API

Overview

Lighter OI Aggregator answers a question every Lighter dashboard needs: how much open interest is there across the whole exchange right now? Lighter's market-stats stream reports open interest market by market, so an exchange-wide figure means summing every market, and keeping a socket open in every client just to add those numbers up is wasteful.

The service splits the problem into two small processes. A worker connects on a schedule, reads one complete snapshot of every market, computes the total and disconnects. A stateless HTTP API reads the stored figure from Redis, so clients get the number with one request and no streaming code.

What it does

  • Subscribes to Lighter's market_stats/all channel and reads the initial snapshot of every market
  • Sums open interest across all markets and doubles it, matching the two-sided total Lighter's own interface shows
  • Persists the result to Upstash Redis, overwriting the previous value each cycle
  • Repeats every 15 minutes, so newly listed or delisted markets are picked up without code changes
  • Serves the latest total from GET /api/open-interest, with CORS enabled for browser clients

Technical breakdown

Snapshot worker

Instead of holding a long-lived WebSocket, the worker opens a fresh connection each cycle, subscribes to market_stats/all and waits for the subscription snapshot. It accepts both payload shapes the channel can send (a map of market id to stats, or a single market object), keeps the latest open interest per market id, saves the doubled total once and closes the socket. A 60-second timeout guarantees a stalled connection can never block the next cycle.

Storage and API

Upstash Redis holds a single key, total_open_interest_usd, which is the only state the worker and the API share, so the two never depend on each other's memory or process. The Express server is stateless: one route reads the key and returns { total_open_interest_usd }, with failures mapped to a 500 response. One npm script starts both processes together.

Technical challenges

  1. 01

    Normalising Lighter's open-interest figure

    The market stats stream reports open interest for one side of each market, so a plain sum comes out at half the number traders see in Lighter's app. The service doubles the total once, at write time, so every consumer reads the same figure and none of them has to know about the quirk.

  2. 02

    Choosing snapshots over a permanent stream

    Total open interest moves slowly compared with prices, so a permanent socket would add reconnection logic and memory for no real benefit. Reading one snapshot per cycle keeps the worker simple and cheap, and because each cycle sums whatever markets exist at that moment, listings and delistings are handled automatically.

  3. 03

    Never letting one cycle block the next

    A network stall during the snapshot could leave the worker waiting indefinitely. Every cycle resolves on close, on error or after a 60-second timeout, whichever comes first, so a bad connection costs at most one refresh.

  4. 04

    Handling two payload shapes

    The same channel can deliver a map of markets or a single market object depending on the message. The parser normalises both into one record keyed by market id before summing, so a market that appears in more than one message is never counted twice.

Who it is for

Analytics dashboards, trading tools and bots that need Lighter's total open interest as a single number, without running their own WebSocket client or aggregation logic.

Learnings

  • Match the refresh cadence to how fast the metric actually moves
  • Normalise upstream quirks once, at write time, not in every client
  • Separate processes should share state through a store, not memory
  • Every network wait needs a timeout
  • Small single-purpose services are easy to reason about and redeploy

More projects

Screenshot of LighterPulse

Analytics hub for Lighter.xyz: dashboards, a block explorer, exchange stats and cross-venue funding comparison.

  • Next.js
  • TypeScript
  • Tailwind CSS
Live
Screenshot of Lighter Liquidations

Backend service

Lighter Liquidations

Streaming pipeline that ingests Lighter liquidations, dedupes replayed batches and serves a rolling 24h total.

  • Node.js
  • WebSockets (Lighter streams)
  • Upstash Redis
Open source
View all projects