Custom indicator
Define an indicator with defineIndicator and use it like a built-in one.
"use client";
import { ChartCanvas, Pane } from "@reincharts/core";
import { CandlestickSeries } from "@reincharts/series";
import { bollinger, defineIndicator, Indicator } from "@reincharts/indicators";
import { Grid, XAxis, YAxis } from "@reincharts/axes";
import { Crosshair } from "@reincharts/coordinates";
import { Legend } from "@reincharts/tooltip";
import { daily } from "@/lib/data";
import { useDocsChartTheme } from "@/lib/use-docs-theme";
/** Bollinger %B: where the close sits inside the bands (0 = lower band, 1 = upper band). */
const percentB = defineIndicator<{ period: number; multiplier: number }>({
id: "percentB",
name: "Bollinger %B",
shortName: "%B",
category: "volatility",
overlay: false,
inputs: {
period: { type: "number", label: "Length", default: 20, min: 1 },
multiplier: { type: "number", label: "StdDev", default: 2, min: 0.1, step: 0.1 },
},
outputs: {
pb: {
type: "line",
label: "%B",
color: "#0ea5e9",
// red above the upper band, green below the lower band
colorFn: (v) => (v > 1 ? "#ef4444" : v < 0 ? "#10b981" : "#0ea5e9"),
},
},
levels: [{ value: 1 }, { value: 0.5 }, { value: 0 }],
fills: [{ between: [1, 0], color: "rgba(14, 165, 233, 0.08)" }],
scale: { precision: 2 },
calculate(data, { period, multiplier }) {
const { upper, lower } = bollinger(data, { period, multiplier });
return {
pb: data.map((d, i) => {
const [u, l] = [upper[i], lower[i]];
return u === undefined || l === undefined || u === l ? undefined : (d.close - l) / (u - l);
}),
};
},
});
export default function CustomIndicator() {
const theme = useDocsChartTheme();
return (
<ChartCanvas data={daily} theme={theme} barSpacing={8}>
<Pane size={2}>
<Grid />
<CandlestickSeries name="MSFT" />
<YAxis />
<Legend title="MSFT · 1D" />
</Pane>
<Pane>
<Grid vertical={false} />
<Indicator definition={percentB} inputs={{ period: 20 }} />
<YAxis />
<Legend />
</Pane>
<XAxis />
<Crosshair />
</ChartCanvas>
);
}defineIndicator types an IndicatorDefinition: inputs, outputs, levels, fills and a calculate function. Pass it to
<Indicator> and you get rendering, autoscale, a legend item, magnet snapping and style overrides for free, exactly like
the built-in indicators. This example is Bollinger %B: where the close sits inside the bands.
import { bollinger, defineIndicator, Indicator } from "@reincharts/indicators";
const percentB = defineIndicator<{ period: number; multiplier: number }>({
id: "percentB",
name: "Bollinger %B",
shortName: "%B", // legend: "%B 20 2"
category: "volatility",
overlay: false, // needs its own pane
inputs: {
period: { type: "number", label: "Length", default: 20, min: 1 },
multiplier: { type: "number", label: "StdDev", default: 2, min: 0.1, step: 0.1 },
},
outputs: {
pb: {
type: "line",
label: "%B",
color: "#0ea5e9",
colorFn: (v) => (v > 1 ? "#ef4444" : v < 0 ? "#10b981" : "#0ea5e9"),
},
},
levels: [{ value: 1 }, { value: 0.5 }, { value: 0 }],
fills: [{ between: [1, 0], color: "rgba(14, 165, 233, 0.08)" }],
calculate(data, { period, multiplier }) {
const { upper, lower } = bollinger(data, { period, multiplier });
return {
pb: data.map((d, i) =>
upper[i] === undefined || lower[i] === undefined ? undefined : (d.close - lower[i]!) / (upper[i]! - lower[i]!),
),
};
},
});
<Pane>
<Indicator definition={percentB} inputs={{ period: 20 }} />
</Pane>;Definition reference
- inputs are user settings. Their keys are passed to
calculate, andtype("number","source","select","boolean","color") tells settings dialogs how to edit them. The declaration order is the legend name order. - outputs are the arrays returned by
calculate, keyed the same way, drawn as"line","histogram","area","dots"or"step".colorFncolors histograms / dots per bar,offsetshifts an output (Ichimoku),hiddenoutputs start hidden. - levels are horizontal reference lines, fills fill between two outputs, an output and a value, or two values.
- scale gives hints: a
domainalways included withfitDomain, the legendprecision,compactlabels. - calculate(data, inputs) is memoized on the chart data and inputs.
To make an indicator available by id (saved layouts, pickers), add it to your own map next to the built-in
indicators registry: const myIndicators = { ...indicators, percentB }. With <TradingChart>, pass it as
extraIndicators.
Helpers for settings UIs
defaultInputs(definition) returns the default inputs, resolveInputs(definition, partial) merges user inputs over
them, and indicatorName(definition, inputs) builds the legend name ("%B 20 2"). priceSources and
movingAverageTypes list the options of "source" and moving average "select" inputs.