chart-setup

star 3

Wire TradingView lightweight-charts into a React app — candlestick + volume series, dark theme, dynamic resize. Use when the user wants to add a price chart, swap a Recharts/Chart.js chart for lightweight-charts, or build a custom indicator on top of an existing chart.

TradersPost By TradersPost schedule Updated 5/15/2026

name: chart-setup description: Wire TradingView lightweight-charts into a React app — candlestick + volume series, dark theme, dynamic resize. Use when the user wants to add a price chart, swap a Recharts/Chart.js chart for lightweight-charts, or build a custom indicator on top of an existing chart.

chart-setup

Add a production-quality price chart to a React app using TradingView's lightweight-charts library. This is what powers the main ChartCanvas in command-dash.

When to use

Trigger this skill whenever the user wants to:

  • Show an OHLC / candlestick chart for a stock or crypto symbol
  • Add volume bars under a price chart
  • Theme an existing lightweight-charts instance to a dark dashboard palette
  • Wire a chart to a fetch('/api/...') source that returns { time, open, high, low, close, volume }[]

Install

npm install lightweight-charts

lightweight-charts v5+ uses the modular series API (addSeries(CandlestickSeries, ...)) — older v4 code that calls addCandlestickSeries(...) will not work. If the project is on v4, either upgrade to v5 or use the v4 API throughout.

Drop-in component

Create src/components/ChartPanel.tsx:

import { useEffect, useRef, useState } from 'react'
import {
  createChart,
  CandlestickSeries,
  HistogramSeries,
  type IChartApi,
  type ISeriesApi
} from 'lightweight-charts'

interface Bar {
  time: number          // unix seconds (UTCTimestamp)
  open: number
  high: number
  low: number
  close: number
  volume: number
}

interface Props {
  symbol: string
  fetchBars: (symbol: string) => Promise<Bar[]>
}

export function ChartPanel({ symbol, fetchBars }: Props) {
  const containerRef = useRef<HTMLDivElement | null>(null)
  const chartRef = useRef<IChartApi | null>(null)
  const candleRef = useRef<ISeriesApi<'Candlestick'> | null>(null)
  const volRef = useRef<ISeriesApi<'Histogram'> | null>(null)

  useEffect(() => {
    if (!containerRef.current) return
    const chart = createChart(containerRef.current, {
      layout: { background: { color: '#1d2939' }, textColor: '#98a2b3' },
      grid: { vertLines: { color: '#293548' }, horzLines: { color: '#293548' } },
      rightPriceScale: { borderColor: '#344054' },
      timeScale: { borderColor: '#344054', timeVisible: true, secondsVisible: false },
      crosshair: { mode: 1 },
      autoSize: true
    })
    candleRef.current = chart.addSeries(CandlestickSeries, {
      upColor: '#12b76a', downColor: '#f04438',
      wickUpColor: '#12b76a', wickDownColor: '#f04438',
      borderVisible: false
    })
    volRef.current = chart.addSeries(HistogramSeries, {
      priceFormat: { type: 'volume' },
      priceScaleId: 'volume_scale',
      color: '#344054'
    })
    chart.priceScale('volume_scale').applyOptions({ scaleMargins: { top: 0.82, bottom: 0 } })
    chartRef.current = chart
    return () => chart.remove()
  }, [])

  useEffect(() => {
    let cancelled = false
    fetchBars(symbol).then((bars) => {
      if (cancelled || !candleRef.current || !volRef.current) return
      candleRef.current.setData(bars.map((b) => ({
        time: b.time as never, open: b.open, high: b.high, low: b.low, close: b.close
      })))
      volRef.current.setData(bars.map((b) => ({
        time: b.time as never,
        value: b.volume,
        color: b.close >= b.open ? 'rgba(18, 183, 106, 0.35)' : 'rgba(240, 68, 56, 0.35)'
      })))
      chartRef.current?.timeScale().fitContent()
    })
    return () => { cancelled = true }
  }, [symbol, fetchBars])

  return <div ref={containerRef} className="w-full h-full" />
}

Key requirements

  1. autoSize: true in createChart options — fills its container and re-fits on resize. No manual resizeObserver plumbing required.
  2. Container must have an explicit heighth-full only works if every ancestor is also height-constrained (flexbox or grid).
  3. time is unix seconds (UTCTimestamp) — not milliseconds, not ISO strings. Divide JS Date.now() by 1000.
  4. Sort bars ascending by time before calling setData. Polygon and most providers return ascending if you pass sort=asc.
  5. Cast time with as never in TS to avoid type friction with lightweight-charts' branded Time type.

Color palette (TradersPost dark navy)

Token Value Use
bg-card #1d2939 chart background
border #344054 axis + scale borders
text-secondary #98a2b3 axis text
grid lines #293548 both vert + horz
up candle / bull #12b76a wickUp, body up
down candle / bear #f04438 wickDown, body down
accent / line #2e90fa overlay lines, equity curves

Reference

The canonical implementation in command-dash is apps/web/src/components/dashboard/ChartCanvas.tsx. It includes:

  • Controlled props for symbol, timeframe, chart type (Candles / Bars / Line / Area / Heikin Ashi), and indicator overlays (SMA / EMA)
  • Vite proxy to a FastAPI backend that returns Polygon OHLC
  • A requestAnimationFrame size-wait pattern so charts created before the grid layout finalizes still render correctly

For equity-curve charts (single line series, no volume), see the OverviewTab inside apps/web/src/components/dashboard/StrategyReport.tsx.

Install via CLI
npx skills add https://github.com/TradersPost/traderspost-command-dash --skill chart-setup
Repository Details
star Stars 3
call_split Forks 3
navigation Branch main
article Path SKILL.md
More from Creator