Tools are drawings/annotations that are superimposed onto the chart. They come in all kinds of shapes and sizes. For example, a Tool could be a simple rectangle or ellipsis, a line segment, or something more complicated as used in technical chart analysis.
By default, calling Controller.addTool will just start interactive placement. By clicking anywhere in a chart, the Tool can be placed by the user.
const tool = controller.addTool('TrendLine');
It is also possible to place tools programmatically without depending on user interaction. Programmatic placement requires two additional parameters:
A simple Rectangle could be placed as follows:
const tool = controller.addTool('ShapeRectangle', [
[timestamp1, value1], // top left
[timestamp2, value2], // top right
[timestamp3, value3], // bottom right
[timestamp4, value4], // bottom left
], controller.getObject({objType: 'chart'}));
The number of handles and their respective meaning may differ from tool to tool.
If you want to modify the placement of a Tool after the initial creation, you can use Tool.setHandles.
Each tool is associated with a price scale. This controls which Y-axis the tool's coordinates are interpreted against. The available scales are the primary scale, the secondary scale (visible when in use), and invisible scales (no displayed axis, but usable as long as a layerable is bound to them).
Pass a targetScale argument to addTool using the ChartEScale constants:
import { ChartEScale } from '@stock3/charting-bundle-chartbreaker';
// primary scale (default)
const tool1 = controller.addTool('TrendLine', handles, chart, ChartEScale.PRIMARY);
// secondary scale (created automatically if it does not exist yet)
const tool2 = controller.addTool('TrendLine', handles, chart, ChartEScale.SECONDARY);
targetScale is only applied during programmatic placement. It has no effect during interactive placement (i.e. when handles / target are omitted).
controller.moveObject(tool, chart, ChartEScale.SECONDARY);
const scaleId = controller.getObjectScale(tool); // Scale object, scale.get('id') is of ChartEScale
Tools that set similarValues: true in their configuration automatically follow the reference timeseries when it is moved between scales. This keeps such tools visually aligned with the series they annotate without any extra action from the consumer.
A tool can only be placed on a scale that has at least one layer (timeseries or indicator) bound to it. Scales derive their Y-axis bounds exclusively from their layers, so a scale without any layers has no bounds and cannot correctly position a tool.
Concretely, this means:
addTool with targetScale set to an unused scale id will place the tool on the primary scale instead.moveObject with targetScale === true (which would create a fresh hidden scale with no layers) is blocked and returns false for tools.Every tool has access to Chart.getReferenceOHLCInput(), which always returns the OHLC data of the chart's main timeseries. Some tools - for example AnchoredVWAP and LinRegLine - derive their values directly from this data and are therefore inherently tied to the same scale as the main timeseries. Placing such a tool on the secondary scale while the main timeseries is on the primary scale would produce coordinates that do not align with the underlying data.
Controller.fitScaleToObjects adjusts the Y-axis range so that the given tools are fully visible. It correctly targets the scale each tool lives on, so it works regardless of whether a tool is on the primary or secondary axis:
controller.fitScaleToObjects([tool1, tool2]);