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.
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-codesclose, 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-up —
periodis a number of bars; the firstperiod − 1rows areundefined(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):
| Study | Appends | Computes |
|---|---|---|
sma | sma | Simple moving average (mean of the last period bars). |
ema | ema | Exponential moving average (α = 2/(period+1)). |
bollinger | bbMiddle / bbUpper / bbLower | SMA ± stdDev×population σ (default 2). |
envelope | envMiddle / envUpper / envLower | Moving-average envelope, middle × (1 ± percent/100). |
rollingStdev | stdev | Rolling population standard deviation. |
rollingMin | min | Rolling minimum (Donchian lower). |
rollingMax | max | Rolling maximum (Donchian upper). |
rollingPercentile | p{q} | Rolling q-th percentile (linear interpolation). |
zScore | zscore | (value − SMA) / σ, standardized deviation. |
percentChange | pctChange | Rate 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:
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;showOHLCfans the full open/high/low/close to the readout. -
Crosshair —
cursor="crosshair"on<ChartContainer>gives the trading-terminal reticle with on-axis pills. -
A volume pane — a second
<ChartRow>with a<BarChart>on thevolumecolumn 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>acalendarand closed-market gaps collapse; see the Trading-time axis. -
A live price pill — a
YAxisIndicatordriven bycreateLiveValuepins 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 / ChartIQ | pond-ts |
|---|---|
| Indicator / Study | the 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-pane | a second <ChartRow> with <BarChart column="volume"> |
| Pane / sub-chart | a <ChartRow> (Layout) |
| Crosshair | cursor="crosshair" |
| OHLC legend / data window | <Candlestick showOHLC> |
| Last-price line / label | YAxisIndicator + createLiveValue |
| Session separator | sessionDividers on <ChartContainer> |
| Regular trading hours (no gaps) | the calendar prop + a TradingCalendar |
See also
- @pond-ts/financial — constructing a
TradingCalendarand the studies API surface (the package side). - Candlestick · Trading-time axis — the two primitives this hub assembles.
- Storybook:
Charts/Candlestick→ ScenarioPriceVolume — the price + volume two-row scenario. - The generated API reference — every study's full signature.