npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@christtrade/depth

v0.20.0

Published

Depth - the charting library built for ChristTrade.

Readme

A trading chart for React: canvas rendering, a plugin and scripting runtime, a strategy backtester, and a local L3 matching engine. It's the chart behind ChristTrade.

Depth ships the whole terminal, not drawing primitives: toolbar, symbol switcher, drawing tools, indicators, order ticket, playback and settings. You pass it an IDataAdapter and a container with a height.

depth.mjs is ~1.2 MB minified. React 18 or 19 is the only peer dependency; Radix, Lucide, Luxon, the workers and the fonts are bundled. If you need a 45 KB line plotter, use Lightweight Charts instead.

Install

npm i @christtrade/depth
npm i react react-dom   # if you don't already have them

Quick start

SimulatedMarketAdapter generates synthetic data, so this runs without a data source:

import { useDepthChart, SimulatedMarketAdapter } from '@christtrade/depth';
import '@christtrade/depth/depth.css';

export default function MyChart() {
    const { chart, element } = useDepthChart({
        dataAdapter: new SimulatedMarketAdapter(),
        // must match what the adapter resolves. a symbol it doesn't know leaves
        // the chart stuck on "Getting market data..."
        symbol: 'SIM-MARKET',
        horizon: Date.now(),
        initialLoad: {
            start: Date.now() - 1000 * 60 * 60 * 24,
            end: Date.now(),
        },
    });

    return <div style={{ width: '100%', height: '100%' }}>{element}</div>;
}

element is the chart. chart is the controller: timeframes, symbols, drawings, plugins, serialize() / restore(). Types ship with the package.

Setup gotchas

  • The chart fills its container. height: auto collapses it to 0.
  • Dark mode is a dark class on any ancestor or on <html>. Without it you get the light theme.
  • depth.css includes Tailwind preflight (a CSS reset). If it fights your styles, import @christtrade/depth/depth.nopreflight.css instead.
  • Workers are embedded as blob URLs, so a strict CSP needs worker-src blob:.
  • From npm create vite: add class="dark" to <html> and drop import './index.css' from main.jsx. The template centres and pads #root.

Features

  • Chart types: candles, hollow candles, Heikin-Ashi, bars, line, area, step, baseline, and footprint on L3 data. Plugins can register more.
  • Drawing tools: trend line, ray, extended line, horizontal and vertical lines and rays, cross line, info line, trend angle, parallel channel, Fib retracement, rectangle, triangle, text, long/short position and fixed volume profile.
  • 21 built-in indicators, written against the same plugin API third parties get.
  • Multi-pane and multi-chart layouts with synced crosshairs.
  • Playback: scrub, step and replay sessions bar by bar.
  • Trading: order ticket, quick-trade buttons, positions and P&L on the chart, and a local L3 matching engine with real fee schedules for simulated fills.
  • Sessions: trading hours, holidays and subsessions. Closed time is cut out of the x axis.
  • Themes: built-in dark and light themes, or register your own.

Architecture

Data

An IDataAdapter implements resolveSymbol, searchSymbols and fetchBars, plus optional subscribeRealtime and getCapabilities. Each adapter declares the data level it serves:

| Level | Data | Also serves | | --- | --- | --- | | l3 | MBO events | tick, ohlcv, l2 | | tick | trades | ohlcv | | ohlcv | bars | | | l2 | book snapshots | |

A plugin or feature declares the level it needs, and on a symbol that can't serve it, it's refused with a reason instead of breaking. The chunk processors (processOhlcvChunk, processTickChunk, processL2Chunk, processL3Chunk) are exported, so an adapter doesn't reimplement aggregation. Plugins can add data sources at runtime with registerDataSource, which is how bring-your-own-API-key providers work.

The DataEngine keeps one store per symbol and pages history in as you pan. Every chart on that symbol reads the same store.

Storage

OHLCV lives in a BarStore: six Float64Array columns (time, open, high, low, close, volume, plus bid/ask once a bar has them), sorted by time.

  • 48 bytes a bar. A { time, open, high, low, close, volume } object is ~176, and each one is something for the GC to walk.
  • Capacity doubles on growth, and the spare room is split between both ends, so append and prepend are amortized O(bars added). Paging history in never copies what's loaded.
  • lowerBound / upperBound binary search on time.
  • When a chart's timeframe is the resolution the data came in at, the chart draws from the engine's store directly. Other timeframes get one aggregated copy.

The column views are replaced when the store grows. Read bars.close off the store each time, and key caches on bars.version, which bumps on every mutation.

L3 events are packed into 20 bytes each in a CompactBuffer (BigInt64 timestamp, int32 price, uint32 size, uint24 order id, packed action/side), so 30M events take ~600 MB instead of ~5 GB as objects.

Rendering

Four stacked canvases: background, base (candles, grid, indicators), drawings and ui (crosshair, axis labels, price pills). One rAF loop paints only the layers marked dirty, so an idle chart costs close to nothing.

Time is BigInt nanoseconds. SessionMapper maps real time to "market time", with closed sessions taken out, and the transformer maps that to pixels.

Per frame, drawing is O(visible bars), and lookups by time (status line, magnet snap, y-axis autofit) are O(log n) in loaded bars.

Workers

  • script.worker: user scripts and strategies, off the main thread.
  • slice.worker: decodes L3 ranges and merges history pages.
  • heatmap.worker: builds book snapshots and renders the depth heatmap to an OffscreenCanvas.
  • An indicator worker for heavy indicator math.

Performance

Ryzen 5 7500F, Chrome, 1m bars.

  • 1M bars loaded, ~300 on screen: panning holds 240fps. Draw, status line, autofit and snap cost the same as with 20k loaded (see the perf guard below).
  • Heap with 1M bars loaded, in ChristTrade: ~480 MB.
  • Backtest over 2M bars (~4 years of BTC 1m) through the script worker: 207-265ms. Stats are running (Welford) and the equity curve is capped, so it holds ~1.1 MB at 2M bars.
  • processL3Chunk: ~1.5M MBO events/sec on synthetic data.

Perf guard

npm run perf:guard runs in CI. It bundles the draw, status line, autofit, snap and prepend paths with esbuild, times each at 20k and at 1M loaded bars with ~300 on screen, drawing to a no-op canvas, and fails if any is 5x slower at 1M, or if a BarStore paged in to 1M bars takes more than 72 bytes a bar including spare capacity:

ok   draw candles   118.0µs -> 85.4µs  0.7x
ok   status line    0.8µs -> 0.9µs  1.1x
ok   autofit        2.0µs -> 1.9µs  1.0x
ok   magnet snap    1.0µs -> 0.9µs  0.9x
ok   page prepend   0.96ms -> 1.04ms  1.1x
ok   bytes/bar      66.7 at 1M, spare room included (max 72)

Left column is 20k bars, right is 1M.

Plugins

Plugins are JavaScript modules, or scripts written in the built-in editor on christtrade.com. They can add indicators, chart types, drawing tools, panels, toolbar items, data sources and render overlays, and can offload heavy math to WebAssembly.

A plugin declares the capabilities and network origins it needs, and ctx only exposes those, so you can show a user what a plugin wants before it runs. It isn't a sandbox: plugin code runs in your page. Read SECURITY.md before running other people's plugins.

Strategies run in the script worker. StrategyEngine does intrabar fills, parameter sweeps and walk-forward analysis.

Full docs: docs.christtrade.com/depth.

Development

npm test              # node:test via tsx
npm run perf:guard
npx tsc -p tsconfig.json --noEmit
npm run dist          # build dist/, smoke test it

CI runs tests, typecheck and the perf guard on Node 20 and 22.

Status

Pre-1.0. It runs in production at ChristTrade, but the public API still moves and minor versions can break it. 1.0 is the API freeze. Breaking changes are marked in CHANGELOG.md.

Contributing

Issues are welcome: bug reports, reproductions, API questions.

Pull requests are by invitation. Depth is a single-author library. If you found something broken, open an issue, and if a PR is the right fix I'll ask for one. Unsolicited PRs usually get closed and reopened as issues, which isn't a comment on the code.

Attribution

Depth draws a small ChristTrade mark in the corner of the chart. Apache-2.0 doesn't require you to keep it, and you can remove it. I'm asking you not to: it's the only thing the library asks for, and it's how people find it.

The name is separate. Apache-2.0 grants no trademark rights (section 6), so you can fork Depth but can't ship it as "ChristTrade Depth". See NOTICE.

License

Apache License 2.0 - Copyright 2026 Carl Sterner.