Skip to main content

Getting started

Install​

tidal-ts needs Node 22 or later and React 18 or 19. The pond-ts packages are peer dependencies: your app installs them once, on one version, so there is only ever one copy of pond-ts in the bundle. Two copies break silently, because a series made by one copy is not recognised by the other.

npm install --save-exact @tidal-ts/core @tidal-ts/chart @pond-ts/process@0.73.0
npm install pond-ts@~0.73.0 @pond-ts/financial@~0.73.0 @pond-ts/charts@~0.73.0 @pond-ts/react@~0.73.0

The pond versions must be the ones tidal-ts was built against, which is why they are named: a plain npm install pond-ts takes the newest release, and npm refuses it when tidal-ts asks for an older one. Each tidal-ts release names its pond version in its peer dependencies (npm view @tidal-ts/chart peerDependencies). Add @tidal-ts/terminal (with xstate and @xstate/react) only if you want its state machine.

A first chart​

Candles, a 20-bar moving average over them, and a 14-bar RSI in a panel of its own. The data is the practice series that ships with @tidal-ts/core, a seeded random walk, so it draws the same picture every time.

first-chart.tsx
import { defaultTheme, type ChartTheme } from '@pond-ts/charts';
import { DEMO_INSTRUMENTS, deriveId, generatePriceSeries, type DeriveSpec } from '@tidal-ts/core';
import {
prepareChart,
TimeSeriesChart,
type ChartSeries,
type SeriesConfig,
} from '@tidal-ts/chart';

// 1. Data: a pond TimeSeries of daily bars. Here a seeded practice walk; in an
// app, your adapter turns your API's response into the same shape.
const price = generatePriceSeries(DEMO_INSTRUMENTS[0]!, { bars: 200 }) as unknown as ChartSeries;

// 2. Studies are specs: an op, its inputs, its settings. `deriveId` names the
// column the study's values land in.
const sma: DeriveSpec = { op: 'sma', inputs: ['close'], params: { period: 20 } };
const rsi: DeriveSpec = { op: 'rsi', inputs: ['close'], params: { period: 14 } };

// 3. Configs say what to draw, in which colour, on which axis. Colours here are
// plain CSS; a host with a palette passes its own keys and a `resolveColor`.
const base = { axis: 'L', visible: true, value: null, unit: '', source: 'price' } as const;
const candles: SeriesConfig = {
...base,
id: 'price',
column: 'close',
label: 'Price',
color: '#8b93a8',
style: 'candle',
colorMode: 'split',
riseColor: '#3fb68b',
fallColor: '#e5534b',
};
const smaLine: SeriesConfig = {
...base,
id: 'sma',
column: deriveId(sma),
derive: sma,
label: 'SMA 20',
color: '#f0a93f',
style: 'line',
};
const rsiLine: SeriesConfig = {
...base,
id: 'rsi',
column: deriveId(rsi),
derive: rsi,
label: 'RSI 14',
color: '#a99cf5',
style: 'line',
};

// 4. The prepare step computes the studies and seats each config on its row.
const { rows, sources } = prepareChart({ price: { series: price } }, [
{ id: 'main', configs: [candles, smaLine] },
{ id: 'rsi', configs: [rsiLine] },
]);

// 5. The theme is a prop too: the chart reads no CSS and no provider.
const themeFor = (dark: boolean): ChartTheme => ({
...defaultTheme,
background: undefined,
axis: {
...defaultTheme.axis,
label: dark ? '#868fa6' : '#55607a',
grid: dark ? 'rgba(233,237,246,0.08)' : 'rgba(17,24,39,0.10)',
},
});

export default function FirstChart({ dark = true }: { dark?: boolean }) {
return (
<TimeSeriesChart
rows={rows.map((r) => ({ ...r, height: r.id === 'main' ? 280 : 140 }))}
sources={sources}
ohlcSources={['price']}
theme={themeFor(dark)}
colorScheme={dark ? 'dark' : 'light'}
/>
);
}

What to notice:

  • A study is data, not code. { op: 'rsi', inputs: ['close'], params: { period: 14 } } is the whole description. The prepare step computes it, and deriveId gives the name of the column its values land in.
  • The chart decides nothing about looks. Colours sit in the configs and the theme is a prop. Pass palette names instead of CSS colours and give the chart a resolveColor to map them.
  • Guide lines come with the study. The RSI's 30 and 70 lines are drawn because the study declares them, not because the example asked.

Next​