Trend Lines in a JavaScript or React Financial Chart
Trend lines are the most used drawing on a trading chart: a line through two or more highs or lows that shows the direction of the trend, and where support or resistance may be. This page explains what trend lines are, how they work and how traders use them, and shows how to draw trend lines and horizontal lines on a JavaScript or React financial chart with SciTrader.js, by point and click or in code, with live examples you can edit in CodePen.
The trend line tools are three of the 34 drawing tools in the SciTrader SDK. For the full list, with each tool's id, annotation class and number of points, see Every Drawing Tool. To add drawing tools with point and click or in code, see Add Drawing Tools to a Financial Chart.
What Is a Trend Line?
A trend line connects two or more price points to show the trend:
- A rising trend line (support) connects higher lows. Price above it is in an uptrend.
- A falling trend line (resistance) connects lower highs. Price below it is in a downtrend.
- A horizontal line marks one price, such as an earlier high or low, where price has turned before.
SciTrader's line tools, in the Lines group:
| Tool | Annotation class | Points | Use it for |
|---|---|---|---|
| Extended Line | Extended | 2 | A trend line, optionally extended past its points to project it forward |
| Horizontal Line | Horizontal | 1 | A support or resistance level |
| Poly Line | Poly | 2 or more | A line segment between two points, or a path through several |
| Vertical Line | Vertical | 1 | Marking a time, such as an event |
| Cross Line | Cross | 1 | Marking a price and a time together |
| Angle Line | Angle | 2 | A line with its angle shown |
How Is a Trend Line Drawn?
A trend line through the points and , where is a candle's time and a price, is at price
at time . Extending it, with extendEnd: true, continues it past the second point using the same slope, so it shows where the trend line will be in future candles.
Traders draw a trend line through swing points: clear lows for a rising line, clear highs for a falling one. The more times price touches a line without breaking it, the more significant it is taken to be.
How to Use Trend Lines
These are the common uses. They are conventions traders use, not signals that hold every time:
- Trend direction. A rising trend line under the lows confirms an uptrend; a falling one over the highs, a downtrend.
- Support and resistance. Price often bounces off a trend line or horizontal line it has touched before.
- Breakouts. A close through a trend line, especially on high volume, is read as the trend weakening or reversing.
- Role reversal. Once broken, a support line often becomes resistance, and resistance becomes support.
- Channels. A parallel line on the other side of the price forms a channel; see the Channel tools.
Drawing Trend Lines with Point and Click
Start the tool with selectTool, from a button or the Drawing Toolbar. For a trend line the user clicks two points; for a horizontal line, one:
import { ETradingAnnotationType } from "scichart-financial-tools";
const drawing = DrawingToolsController.attach(financeChartPanel.financeApi);
drawing.selectTool(ETradingAnnotationType.ExtendedLineAnnotation); // a trend line
drawing.selectTool(ETradingAnnotationType.HorizontalTrendLineAnnotation); // a horizontal line
Adding Trend Lines in Code
Create the annotation with its points, as (time in seconds, price), and add it to the price pane:
import { Point } from "scichart";
import { ExtendedLineAnnotation, HorizontalTrendLineAnnotation } from "scichart-financial-tools";
import { PaneId, registerDrawingTools } from "scichart-scitrader";
registerDrawingTools(); // once, at startup: so the chart lists and saves them
const pricePane = financeChartPanel.financeApi.chartPanes.get(PaneId.DEFAULT).surface;
pricePane.annotations.add(
// A support trend line through two lows, extended to the right
new ExtendedLineAnnotation({
points: [new Point(firstLowTime, firstLow), new Point(secondLowTime, secondLow)],
extendEnd: true,
stroke: "#26a69a",
isEditable: true,
}),
// A resistance level: one point sets its price
new HorizontalTrendLineAnnotation({ points: [new Point(highTime, high)], stroke: "#ef5350", isEditable: true })
);
How to Draw Trend Lines on a JavaScript Financial Chart
An ETH-USD chart from Coinbase with a trend line through the lowest lows of the two halves of the last 90 candles, extended to the right, and a horizontal line at their highest high, both added in code. The buttons start the trend line and horizontal line tools.
The code for this example is found below.
- TS
import "scichart-scitrader/scitrader.css";
import { Point } from "scichart";
import {
ETradingAnnotationType,
ExtendedLineAnnotation,
HorizontalTrendLineAnnotation,
} from "scichart-financial-tools";
import {
CandleInterval,
CoinbaseExchange,
DEFAULT_FEED_ID,
defaultExchangeProvider,
DrawingToolsController,
EDataSourceStringId,
idbPriceStore,
OHLCStudy,
PaneId,
registerDrawingTools,
SciFinanceChart,
SciFinanceChartPanel,
VolumeStudy,
} from "scichart-scitrader";
// This example expects this HTML:
// <div style="display: flex; flex-direction: column; height: 100%">
// <div id="toolbar"></div>
// <div id="chart" style="flex: 1; min-height: 0"></div>
// </div>
// Once, at app startup: register a data source, and the drawing tools' annotation types.
// registerDrawingTools() makes drawings added in code count as user drawings, listed and saved with the chart
defaultExchangeProvider.registerExchange(CoinbaseExchange, idbPriceStore);
registerDrawingTools();
async function createChart() {
const toolbar = document.getElementById("toolbar");
Object.assign(toolbar.style, toolbarStyle);
const financeChartPanel = await SciFinanceChartPanel.create(document.getElementById("chart"), {
exchangeName: CoinbaseExchange.name, // name="Coinbase" matches the built-in CoinbaseExchange
symbol: "ETH-USD",
interval: CandleInterval.SIX_HOURS,
period: "3M",
studies: [new OHLCStudy(), new VolumeStudy()],
});
const chart = financeChartPanel.financeApi;
// 1. Add a drawing in code: create its annotation with its points in data coordinates
// (x: a candle's time in seconds, y: a price), and add it to the price pane's surface
const candles = getCandles(chart);
const pricePane = chart.chartPanes.get(PaneId.DEFAULT).surface;
// A support trend line through the lowest lows of the two halves of the last 90 candles,
// extended to the right, so it projects where support may be next
const n = candles.times.length;
pricePane.annotations.add(
new ExtendedLineAnnotation({
points: [lowest(candles, n - 90, n - 45), lowest(candles, n - 45, n)],
extendEnd: true, // continue past the second point
stroke: "#26a69a",
strokeThickness: 2,
isEditable: true, // the user can select, drag and edit it
})
);
// A horizontal resistance line at the highest high of the last 90 candles: one point sets its price
pricePane.annotations.add(
new HorizontalTrendLineAnnotation({
points: [highest(candles, n - 90, n)],
stroke: "#ef5350",
strokeThickness: 2,
isEditable: true,
})
);
// 2. Point-and-click drawing: the controller adds the drawing modifiers to every pane.
// selectTool starts a tool, and the user clicks the chart to place its points
const drawing = DrawingToolsController.attach(chart);
toolbar.append(
button("Draw a trend line", () => drawing.selectTool(ETradingAnnotationType.ExtendedLineAnnotation)),
button("Draw a horizontal line", () => drawing.selectTool(ETradingAnnotationType.HorizontalTrendLineAnnotation)),
button("Remove all", () => drawing.remove()) // every drawing, the ones added in code too
);
// When the page goes away: drawing.detach(); financeChartPanel.cleanup();
return { financeChartPanel, drawing };
}
// ---------------------------------------------------------------------------------------------
// Not part of the SDK: helpers that read the loaded candles, to choose where to place drawings
// ---------------------------------------------------------------------------------------------
// The loaded candles: times in seconds (the x values of a SciTrader chart), and prices
function getCandles(chart: SciFinanceChart) {
const dm = chart.dataManager;
const values = (name: EDataSourceStringId) => dm.getValues<number>(dm.getSourceIdByName(DEFAULT_FEED_ID, name));
return {
times: dm.xValues,
highs: values(EDataSourceStringId.HIGH),
lows: values(EDataSourceStringId.LOW),
closes: values(EDataSourceStringId.CLOSE),
};
}
type TCandles = ReturnType<typeof getCandles>;
// The lowest low, or highest high, of candles [from, to), as a point on the chart
function lowest(c: TCandles, from: number, to: number): Point {
let i = from;
for (let j = from; j < to; j++) if (c.lows[j] < c.lows[i]) i = j;
return new Point(c.times[i], c.lows[i]);
}
function highest(c: TCandles, from: number, to: number): Point {
let i = from;
for (let j = from; j < to; j++) if (c.highs[j] > c.highs[i]) i = j;
return new Point(c.times[i], c.highs[i]);
}
function button(text: string, onClick: () => void) {
const b = document.createElement("button");
b.textContent = text;
b.onclick = onClick;
Object.assign(b.style, buttonStyle);
return b;
}
// The toolbar uses SciTrader's UI colours: its --st-ui-* CSS variables, with the stylesheet's defaults
const toolbarStyle: Partial<CSSStyleDeclaration> = {
display: "flex",
flexWrap: "wrap",
alignItems: "center",
gap: "8px",
padding: "8px",
background: "var(--st-ui-background, #2b2839)",
borderBottom: "1px solid var(--st-ui-divider, rgba(255,255,255,0.1))",
};
const buttonStyle: Partial<CSSStyleDeclaration> = {
padding: "4px 10px",
border: "1px solid var(--st-ui-divider, rgba(255,255,255,0.1))",
borderRadius: "6px",
background: "var(--st-ui-surface, #373447)",
color: "var(--st-ui-text, #ffffff)",
font: "inherit",
fontSize: "13px",
cursor: "pointer",
};
createChart();
How to Draw Trend Lines on a React Financial Chart
The same chart in React. The drawing is added in onInit, once the chart has loaded its candles, and useDrawingTools gives the buttons their controller.
The code for this example is found below.
- React
import "scichart-scitrader/scitrader.css";
import * as React from "react";
import { Point } from "scichart";
import {
ETradingAnnotationType,
ExtendedLineAnnotation,
HorizontalTrendLineAnnotation,
} from "scichart-financial-tools";
import {
CandleInterval,
CoinbaseExchange,
DEFAULT_FEED_ID,
defaultExchangeProvider,
EDataSourceStringId,
idbPriceStore,
ISciFinanceChartPanelApi,
ISciFinanceChartPanelConfig,
OHLCStudy,
PaneId,
registerDrawingTools,
SciFinanceChart,
VolumeStudy,
} from "scichart-scitrader";
import { SciFinanceChartView, useDrawingTools } from "scichart-scitrader-react";
// Once, at app startup: register a data source, and the drawing tools' annotation types.
// registerDrawingTools() makes drawings added in code count as user drawings, listed and saved with the chart
defaultExchangeProvider.registerExchange(CoinbaseExchange, idbPriceStore);
registerDrawingTools();
export function TrendLinesChart() {
const [chartApi, setChartApi] = React.useState<ISciFinanceChartPanelApi>();
// useDrawingTools attaches a DrawingToolsController once the chart is ready, and detaches it on unmount
const { controller } = useDrawingTools(chartApi);
// Create the config once per mounted chart: studies are live objects bound to one chart
const config = React.useMemo<ISciFinanceChartPanelConfig>(
() => ({
exchangeName: CoinbaseExchange.name, // name="Coinbase" matches the built-in CoinbaseExchange
symbol: "ETH-USD",
interval: CandleInterval.SIX_HOURS,
period: "3M",
studies: [new OHLCStudy(), new VolumeStudy()],
}),
[]
);
// Once the chart has loaded its candles, add a drawing in code: create its annotation with its
// points in data coordinates (x: a candle's time in seconds, y: a price), and add it to the price pane
const onInit = (api: ISciFinanceChartPanelApi) => {
const chart = api.financeApi;
const candles = getCandles(chart);
const pricePane = chart.chartPanes.get(PaneId.DEFAULT).surface;
// A support trend line through the lowest lows of the two halves of the last 90 candles,
// extended to the right, so it projects where support may be next
const n = candles.times.length;
pricePane.annotations.add(
new ExtendedLineAnnotation({
points: [lowest(candles, n - 90, n - 45), lowest(candles, n - 45, n)],
extendEnd: true, // continue past the second point
stroke: "#26a69a",
strokeThickness: 2,
isEditable: true, // the user can select, drag and edit it
})
);
// A horizontal resistance line at the highest high of the last 90 candles: one point sets its price
pricePane.annotations.add(
new HorizontalTrendLineAnnotation({
points: [highest(candles, n - 90, n)],
stroke: "#ef5350",
strokeThickness: 2,
isEditable: true,
})
);
setChartApi(api);
};
// Fills its parent, so give the parent a height
return (
<div style={{ display: "flex", flexDirection: "column", height: "100%" }}>
{/* Point-and-click drawing: selectTool starts a tool, and the user clicks the chart to place its points */}
<div style={toolbarStyle}>
<button style={buttonStyle} disabled={!controller} onClick={() => controller.selectTool(ETradingAnnotationType.ExtendedLineAnnotation)}>
Draw a trend line
</button>
<button style={buttonStyle} disabled={!controller} onClick={() => controller.selectTool(ETradingAnnotationType.HorizontalTrendLineAnnotation)}>
Draw a horizontal line
</button>
<button style={buttonStyle} disabled={!controller} onClick={() => controller.remove()}>
Remove all
</button>
</div>
<div style={{ flex: 1, minHeight: 0 }}>
<SciFinanceChartView config={config} onInit={onInit} />
</div>
</div>
);
}
// ---------------------------------------------------------------------------------------------
// Not part of the SDK: helpers that read the loaded candles, to choose where to place drawings
// ---------------------------------------------------------------------------------------------
// The loaded candles: times in seconds (the x values of a SciTrader chart), and prices
function getCandles(chart: SciFinanceChart) {
const dm = chart.dataManager;
const values = (name: EDataSourceStringId) => dm.getValues<number>(dm.getSourceIdByName(DEFAULT_FEED_ID, name));
return {
times: dm.xValues,
highs: values(EDataSourceStringId.HIGH),
lows: values(EDataSourceStringId.LOW),
closes: values(EDataSourceStringId.CLOSE),
};
}
type TCandles = ReturnType<typeof getCandles>;
// The lowest low, or highest high, of candles [from, to), as a point on the chart
function lowest(c: TCandles, from: number, to: number): Point {
let i = from;
for (let j = from; j < to; j++) if (c.lows[j] < c.lows[i]) i = j;
return new Point(c.times[i], c.lows[i]);
}
function highest(c: TCandles, from: number, to: number): Point {
let i = from;
for (let j = from; j < to; j++) if (c.highs[j] > c.highs[i]) i = j;
return new Point(c.times[i], c.highs[i]);
}
// The toolbar uses SciTrader's UI colours: its --st-ui-* CSS variables, with the stylesheet's defaults
const toolbarStyle: React.CSSProperties = {
display: "flex",
flexWrap: "wrap",
alignItems: "center",
gap: 8,
padding: 8,
background: "var(--st-ui-background, #2b2839)",
borderBottom: "1px solid var(--st-ui-divider, rgba(255,255,255,0.1))",
};
const buttonStyle: React.CSSProperties = {
padding: "4px 10px",
border: "1px solid var(--st-ui-divider, rgba(255,255,255,0.1))",
borderRadius: 6,
background: "var(--st-ui-surface, #373447)",
color: "var(--st-ui-text, #ffffff)",
font: "inherit",
fontSize: 13,
cursor: "pointer",
};
Trend Line Options: ExtendedLineAnnotation and HorizontalTrendLineAnnotation
| Option | What it does |
|---|---|
points | Two points for an extended line or poly line; one for a horizontal, vertical or cross line |
stroke, stroke, stroke | The line's colour, thickness and dash pattern. Without a stroke, the line takes the theme's drawing colour |
extend, extend | Extend the line past its first or last point. On a horizontal line, to the left or right of its point |
is | Whether the user can select, drag and edit it |
The ExtendedLineAnnotation, HorizontalTrendLineAnnotation and AxisAlignedTrendLineAnnotationBase classes, with their TSDoc
/**
* @summary The {@link ExtendedLineAnnotation} provides a {@link MultiPointAnnotationBase | multi-point annotation}
* which draws a two-point trend line over the {@link SciChartSurface}.
* @description
* To add an {@link ExtendedLineAnnotation} to a {@link SciChartSurface}, use the following code:
* ```ts
* const sciChartSurface: SciChartSurface;
* const extendedLineAnnotation = new ExtendedLineAnnotation({
* points: [
* new Point(0, 5), // can also be written as "{ x: 0, y: 5 }"
* new Point(10, 7)
* ],
* stroke: "#3B82F6",
* strokeThickness: 2,
* strokeDashArray: [4, 2],
* extendStart: false,
* extendEnd: true
* });
* sciChartSurface.annotations.add(extendedLineAnnotation);
* ```
* Use `extendStart` and `extendEnd` to project the line beyond its endpoints.
*/
class ExtendedLineAnnotation extends MultiPointAnnotationBase {
/** @inheritDoc */
readonly type: EAnnotationType;
/** @inheritDoc */
readonly surfaceTypes: ESurfaceType[];
constructor(options?: IExtendedLineAnnotationOptions);
/** Gets or sets stroke color. */
get stroke(): string;
set stroke(value: string);
/** Gets or sets stroke thickness. */
get strokeThickness(): number;
set strokeThickness(value: number);
/** Gets or sets stroke dash array. */
get strokeDashArray(): number[];
set strokeDashArray(value: number[]);
/** Gets or sets whether line extends before the first point. */
get extendStart(): boolean;
set extendStart(value: boolean);
/** Gets or sets whether line extends after the second point. */
get extendEnd(): boolean;
set extendEnd(value: boolean);
/** @inheritDoc */
delete(): void;
/** @inheritDoc */
onAttach(scs: SciChartSurface): void;
/** @inheritDoc */
drawWithContext(renderContext: IRenderContext2D, xCalc: CoordinateCalculatorBase, yCalc: CoordinateCalculatorBase, seriesViewRect: Rect, surfaceViewRect: Rect, chartViewRect: Rect): void;
/** @inheritDoc */
onDpiChanged(args: TDpiChangedEventArgs): void;
/** @inheritDoc */
svgStringAdornerTemplate(): string;
/** @inheritDoc */
toJSON(): {
type: EAnnotationType;
options: Required<Omit<IAnnotationBaseOptions, never>>;
};
}
/**
* Draws a horizontal trend line anchored at a single movable point.
*/
class HorizontalTrendLineAnnotation extends AxisAlignedTrendLineAnnotationBase {
/** @inheritDoc */
readonly type: EAnnotationType;
/** @inheritDoc */
readonly surfaceTypes: ESurfaceType[];
constructor(options?: IHorizontalTrendLineAnnotationOptions);
}
/**
* Shared class for 1-point axis-aligned trend lines.
*/
abstract class AxisAlignedTrendLineAnnotationBase extends MultiPointAnnotationBase {
/** Gets or sets the stroke color. */
get stroke(): string;
set stroke(value: string);
/** Gets or sets the stroke thickness. */
get strokeThickness(): number;
set strokeThickness(value: number);
/** Gets or sets the dash pattern for the trend line. */
get strokeDashArray(): number[];
set strokeDashArray(value: number[]);
/** Gets or sets whether the line extends before the anchor point. */
get extendStart(): boolean;
set extendStart(value: boolean);
/** Gets or sets whether the line extends after the anchor point. */
get extendEnd(): boolean;
set extendEnd(value: boolean);
/** @inheritDoc */
delete(): void;
/** @inheritDoc */
onAttach(scs: SciChartSurface): void;
/** @inheritDoc */
drawWithContext(renderContext: IRenderContext2D, xCalc: CoordinateCalculatorBase, yCalc: CoordinateCalculatorBase, seriesViewRect: Rect, surfaceViewRect: Rect, chartViewRect: Rect): void;
/** @inheritDoc */
svgStringAdornerTemplate(): string;
/** @inheritDoc */
toJSON(): {
type: EAnnotationType;
options: Required<Omit<IAnnotationBaseOptions, never>>;
};
}
The IExtendedLineAnnotationOptions, IAxisAlignedTrendLineAnnotationBaseOptions and IPolyLineAnnotationOptions interfaces, with their TSDoc
/** Options for {@link ExtendedLineAnnotation}. */
interface IExtendedLineAnnotationOptions extends IMultiPointAnnotationBaseOptions {
/** Stroke color. */
stroke?: string;
/** Stroke thickness. */
strokeThickness?: number;
/** Optional dash array. */
strokeDashArray?: number[];
/** Extend backwards past the first grip point. */
extendStart?: boolean;
/** Extend forwards past the second grip point. */
extendEnd?: boolean;
}
/** Common options for axis-aligned trend lines. */
interface IAxisAlignedTrendLineAnnotationBaseOptions extends IMultiPointAnnotationBaseOptions {
/** Stroke color. */
stroke?: string;
/** Stroke thickness. */
strokeThickness?: number;
/** Optional dash array. */
strokeDashArray?: number[];
/** Extend before the anchor point in axis direction. */
extendStart?: boolean;
/** Extend after the anchor point in axis direction. */
extendEnd?: boolean;
}
/** Options for {@link PolyLineAnnotation}. */
interface IPolyLineAnnotationOptions extends IMultiPointAnnotationBaseOptions {
/**
* Number of points required when using {@link MultiPointAnnotationPlacementModifier}.
* Default `2`.
*/
placementPointCount?: number;
/** Stroke color. */
stroke?: string;
/** Stroke thickness of the line annotation.
* Default `2`
*/
strokeThickness?: number;
/**
* The strokeDashArray for the {@link LineAnnotation}
*/
strokeDashArray?: number[];
/**
* The primitive triangle fill as an HTML color string.
* Default `null` -> triangles do NOT draw at all.
*/
fill?: string | null;
}
Where to read next
- Add Drawing Tools to a Financial Chart: the other drawing tools, point-and-click drawing and drawings in code.
- Editing Drawings: the quick-edit toolbar, the full editor and edit events.
- Drawing Toolbar and Drawing List: ready-made tools and list for users.
- Trend line annotations: every property and option of each line class, in the
scichart-financial-toolsdocs.