Skip to main content

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.

info

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:

ToolAnnotation classPointsUse it for
Extended LineExtendedLineAnnotation2A trend line, optionally extended past its points to project it forward
Horizontal LineHorizontalTrendLineAnnotation1A support or resistance level
Poly LinePolyLineAnnotation2 or moreA line segment between two points, or a path through several
Vertical LineVerticalTrendLineAnnotation1Marking a time, such as an event
Cross LineCrossLineAnnotation1Marking a price and a time together
Angle LineAngleLineAnnotation2A line with its angle shown

How Is a Trend Line Drawn?​

A trend line through the points (t1,p1)(t_1, p_1) and (t2,p2)(t_2, p_2), where tt is a candle's time and pp a price, is at price

p(t)=p1+(p2−p1)×t−t1t2−t1p(t) = p_1 + (p_2 - p_1) \times \frac{t - t_1}{t_2 - t_1}

at time tt. 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.

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.

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​

OptionWhat it does
pointsTwo points for an extended line or poly line; one for a horizontal, vertical or cross line
stroke, strokeThickness, strokeDashArrayThe line's colour, thickness and dash pattern. Without a stroke, the line takes the theme's drawing colour
extendStart, extendEndExtend the line past its first or last point. On a horizontal line, to the left or right of its point
isEditableWhether 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;
}