Skip to main content

Financial charts

Financial charting in pond is two things that meet on the plot: a library of studies (technical indicators, built on pond as plain series transforms) and the chart assembly that draws them — candles, a volume pane, a session-aware axis, an OHLC readout. This page leads with the studies, then assembles the chart around them.

src/examples/charts-financial-studies.tsx
import {
BandChart,
Candlestick,
ChartContainer,
ChartRow,
Layers,
LineChart,
YAxis,
} from '@pond-ts/charts';
import { bollinger, ema } from '@pond-ts/financial';
import { useSiteChartTheme } from '@site/src/theme/useSiteChartTheme';
import { dailyCandles, dailyCandlesRange } from './lib/gallery-fixtures';

/** Studies are pure `(series, options) => series` functions that **append**
* columns to a bar `TimeSeries` — so they compose, and you draw their output
* as ordinary chart layers. Here `bollinger` adds `bbUpper`/`bbMiddle`/
* `bbLower` and `ema` adds `ema`; the band and line just read those columns
* over the same candles. */
export default function ChartsFinancialStudies({ width }: { width: number }) {
const theme = useSiteChartTheme();
const bars = ema(bollinger(dailyCandles(), { period: 20 }), { period: 10 });

return (
<ChartContainer
range={dailyCandlesRange()}
width={width}
theme={theme}
cursor="crosshair"
>
<ChartRow height={240}>
<YAxis id="price" side="right" format="$,.0f" />
<Layers>
<BandChart
series={bars}
lower="bbLower"
upper="bbUpper"
axis="price"
as="inner"
/>
<LineChart series={bars} column="ema" axis="price" as="secondary" />
<Candlestick series={bars} as="ACME" showOHLC />
</Layers>
</ChartRow>
</ChartContainer>
);
}

Above: daily candles with a Bollinger band and an EMA overlaid — each one a study that appended its columns to the bar series, drawn as an ordinary BandChart / LineChart layer.

The studies library

A study is a pure function (series, options) => series that appends one or more columns to a bar TimeSeries and returns the widened series. They're thin assemblies over a small rolling kernel — they add vocabulary, not new math — and each is checked against an independent reference (a pandas/TA-Lib "oracle") before it ships.

Three conventions make the corpus uniform and composable:

  • column — which field to read (default 'close'). No study hard-codes close, so a study can run over any numeric column — including another study's output (ChartIQ's "Field" idea).
  • output / prefix — what to name the result column(s), so overlays don't collide.
  • Bar-count periods, length-preserving warm-upperiod is a number of bars; the first period − 1 rows are undefined (the chart renders that as a clean gap, so a moving average simply starts once it has enough history).
import { bollinger, ema } from '@pond-ts/financial';

// each study appends columns; they compose left to right
const bars = ema(bollinger(candles, { period: 20 }), { period: 10 });
// bars now has bbUpper / bbMiddle / bbLower (from bollinger) + ema (from ema)

Every study is also a fluent method on TimeSeries once you opt in with import '@pond-ts/financial/fluent'candles.bollinger({ period: 20 }).ema({ period: 10 }).

What's here today

The library is growing — this is the first batch. Each is a batch transform (TimeSeries → TimeSeries + columns):

StudyAppendsComputes
smasmaSimple moving average (mean of the last period bars).
emaemaExponential moving average (α = 2/(period+1)).
bollingerbbMiddle / bbUpper / bbLowerSMA ± stdDev×population σ (default 2).
envelopeenvMiddle / envUpper / envLowerMoving-average envelope, middle × (1 ± percent/100).
rollingStdevstdevRolling population standard deviation.
rollingMinminRolling minimum (Donchian lower).
rollingMaxmaxRolling maximum (Donchian upper).
rollingPercentilep{q}Rolling q-th percentile (linear interpolation).
zScorezscore(value − SMA) / σ, standardized deviation.
percentChangepctChangeRate of change vs periods bars ago, in percent.

Overlay a moving average or a band as chart layers (the hero above); read a scalar study like zScore or pctChange off a second row, or through the cursor.

What's coming

The corpus assessment maps ~124 ChartIQ studies onto ~11 rolling kernels, so the library extends cheaply. The next batch (Phase 1 breadth) adds the oscillators and volume studies most charts reach for: RSI, MACD, ATR (+ATR bands), stochastics, Williams %R, Donchian channel, OBV, VWAP, historical volatility, and momentum / ROC. A later phase covers the stateful ones (PSAR, SuperTrend, ATR trailing stop, ZigZag) and session-anchored studies (pivot points, session-reset VWAP). Adding one is a short, oracle-verified assembly — see the studies/README.md pattern in the package.

Assembling the chart

The chart around the studies is the standard @pond-ts/charts composition — there's no separate "financial mode", just the pieces:

src/examples/gallery-financial.tsx
import {
Candlestick,
ChartContainer,
ChartRow,
Layers,
YAxis,
} from '@pond-ts/charts';
import { useSiteChartTheme } from '@site/src/theme/useSiteChartTheme';
import { dailyCandles, dailyCandlesRange } from './lib/gallery-fixtures';

/** Financial terminal: daily OHLC candles with the crosshair cursor and the
* axis-pill OHLC readout — first-class support, not a bar-chart hack. */
export default function GalleryFinancial({ width }: { width: number }) {
const theme = useSiteChartTheme();
const series = dailyCandles();

return (
<ChartContainer
range={dailyCandlesRange()}
width={width}
theme={theme}
cursor="crosshair"
>
<ChartRow height={220}>
<YAxis id="price" side="right" format="$,.0f" width={50} />
<Layers>
<Candlestick series={series} as="ACME" showOHLC />
</Layers>
</ChartRow>
</ChartContainer>
);
}
  • Candles<Candlestick> reads the four OHLC columns; showOHLC fans the full open/high/low/close to the readout.

  • Crosshaircursor="crosshair" on <ChartContainer> gives the trading-terminal reticle with on-axis pills.

  • A volume pane — a second <ChartRow> with a <BarChart> on the volume column and its own axis:

    <ChartRow height={110}>
    <YAxis id="vol" side="right" format=",.0s" />
    <Layers>
    <BarChart series={bars} column="volume" axis="vol" />
    </Layers>
    </ChartRow>
  • A session-aware axis — hand <ChartContainer> a calendar and closed-market gaps collapse; see the Trading-time axis.

  • A live price pill — a YAxisIndicator driven by createLiveValue pins the last price to the axis edge without re-rendering the chart; see Axis indicators & live values.

Coming from TradingView

If you're arriving from TradingView / ChartIQ, the vocabulary maps like this:

TradingView / ChartIQpond-ts
Indicator / Studythe studies library (sma, bollinger, …)
Field (indicator input)the column option on every study
Candles / OHLC bars<Candlestick>
Bar / Candle / Hollow style<Candlestick variant>
Volume sub-panea second <ChartRow> with <BarChart column="volume">
Pane / sub-charta <ChartRow> (Layout)
Crosshaircursor="crosshair"
OHLC legend / data window<Candlestick showOHLC>
Last-price line / labelYAxisIndicator + createLiveValue
Session separatorsessionDividers on <ChartContainer>
Regular trading hours (no gaps)the calendar prop + a TradingCalendar

See also