Skip to main content

Stop Loss and Take Profit in a JavaScript or React Financial Chart

A stop loss and a take profit are the prices where a trade is closed for a loss or a profit. Drawn on the chart, they show a trade's risk and reward at a glance. This page explains stop-loss and take-profit levels, how the risk/reward ratio is calculated and how traders use it, and shows how to draw stop-loss and take-profit zones on a JavaScript or React financial chart with SciTrader.js, with live examples you can edit in CodePen.

info

The Stop Loss / Take Profit tool is one 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 Are Stop Loss and Take Profit?​

For a long trade (buying, expecting the price to rise):

  • The entry is the price the trade is opened at.
  • The take profit is a price above the entry, where the trade is closed with a profit.
  • The stop loss is a price below the entry, where the trade is closed to limit the loss.

For a short trade, they are the other way round.

SciTrader's Stop Loss / Take Profit tool draws one zone from an entry. The first point is the entry; the second sets the zone's price and its width in time. A zone above the entry is drawn green (takeProfitColor) and one below it red (stopLossColor), so for a long trade, draw one zone up to the target and one down to the stop.

note

The colour follows the side of the entry, not the trade's direction. For a short trade, the take-profit zone is below the entry, so it is drawn red, and the stop-loss zone green.

The Measure tool, in the same group, shows the price change, percentage change and number of candles between two points.

How Is the Risk/Reward Ratio Calculated?​

For a long trade, the reward is the distance from the entry to the target, and the risk the distance from the entry to the stop:

risk/reward=target−entryentry−stopposition size=amount at riskentry−stop\text{risk/reward} = \frac{\text{target} - \text{entry}}{\text{entry} - \text{stop}} \qquad \text{position size} = \frac{\text{amount at risk}}{\text{entry} - \text{stop}}

A trade entered at 80,000 with a target of 84,800 and a stop at 77,600 risks 2,400 to make 4,800: a risk/reward of 2. Risking 1,000 on it, the position size is 1,000 / 2,400 ≈ 0.42 units.

tip

The tool draws the zones, but doesn't show the ratio. Use the Measure tool over each zone to read its size, in price and percent.

How to Use Stop Loss and Take Profit Levels​

These are the common uses. They are conventions traders use, not signals that hold every time:

  • Plan before entering. Setting the stop and target first, and checking the ratio, keeps losing trades small.
  • A minimum ratio. Many traders only take trades with a risk/reward of 2 or more, so a minority of winners can cover the losers.
  • Stops beyond structure. A stop just past a swing low, a trend line or a multiple of the ATR sits outside normal noise.
  • Targets at levels. Targets at earlier highs, Fibonacci levels or a channel's edge are where price often stalls.

Drawing Stop Loss and Take Profit Zones with Point and Click​

Start the tool with selectTool, from a button or the Drawing Toolbar. The user clicks the entry, then the target or stop:

import { ETradingAnnotationType } from "scichart-financial-tools";

const drawing = DrawingToolsController.attach(financeChartPanel.financeApi);
drawing.selectTool(ETradingAnnotationType.StopLossTakeProfitAnnotation);

Adding Stop Loss and Take Profit Zones in Code​

Create a StopLossTakeProfitAnnotation for each zone, with the entry and the target or stop as (time in seconds, price), and add them to the price pane:

import { Point } from "scichart";
import { StopLossTakeProfitAnnotation } from "scichart-financial-tools";
import { PaneId, registerDrawingTools } from "scichart-scitrader";

registerDrawingTools(); // once, at startup: so the chart lists and saves them

const entry = new Point(entryTime, 80000);
financeChartPanel.financeApi.chartPanes.get(PaneId.DEFAULT).surface.annotations.add(
new StopLossTakeProfitAnnotation({ points: [entry, new Point(exitTime, 84800)], isEditable: true }), // take profit: green
new StopLossTakeProfitAnnotation({ points: [entry, new Point(exitTime, 77600)], isEditable: true }) // stop loss: red
);

How to Draw Stop Loss and Take Profit Zones on a JavaScript Financial Chart​

A BTC-USD chart from Coinbase with a long trade added in code: entered at the close 30 candles ago, with a take-profit zone 6% above the entry and a stop-loss zone 3% below it, a risk/reward of 2. The buttons start the Stop Loss / Take Profit and Measure tools.

The code for this example is found below.

import "scichart-scitrader/scitrader.css";
import { Point } from "scichart";
import {
ETradingAnnotationType,
StopLossTakeProfitAnnotation,
} 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: "BTC-USD",
interval: CandleInterval.SIX_HOURS,
period: "1M",
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 long trade entered at the close 30 candles ago, with a take-profit 6% above the entry and a
// stop-loss 3% below it. Point 1 is the entry; point 2 the target, which also sets the zone's width
const n = candles.times.length;
const entry = new Point(candles.times[n - 30], candles.closes[n - 30]);
const end = candles.times[n - 1];
pricePane.annotations.add(
new StopLossTakeProfitAnnotation({ points: [entry, new Point(end, entry.y * 1.06)], isEditable: true }),
new StopLossTakeProfitAnnotation({ points: [entry, new Point(end, entry.y * 0.97)], 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 stop loss / take profit", () => drawing.selectTool(ETradingAnnotationType.StopLossTakeProfitAnnotation)),
button("Measure", () => drawing.selectTool(ETradingAnnotationType.MeasureAnnotation)),
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 Stop Loss and Take Profit Zones 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,
StopLossTakeProfitAnnotation,
} 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 StopLossTakeProfitChart() {
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: "BTC-USD",
interval: CandleInterval.SIX_HOURS,
period: "1M",
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 long trade entered at the close 30 candles ago, with a take-profit 6% above the entry and a
// stop-loss 3% below it. Point 1 is the entry; point 2 the target, which also sets the zone's width
const n = candles.times.length;
const entry = new Point(candles.times[n - 30], candles.closes[n - 30]);
const end = candles.times[n - 1];
pricePane.annotations.add(
new StopLossTakeProfitAnnotation({ points: [entry, new Point(end, entry.y * 1.06)], isEditable: true }),
new StopLossTakeProfitAnnotation({ points: [entry, new Point(end, entry.y * 0.97)], 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.StopLossTakeProfitAnnotation)}>
Draw a stop loss / take profit
</button>
<button style={buttonStyle} disabled={!controller} onClick={() => controller.selectTool(ETradingAnnotationType.MeasureAnnotation)}>
Measure
</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",
};

StopLossTakeProfitAnnotation Options​

OptionWhat it does
pointsTwo points: the entry, then the target or stop. The second also sets the zone's width
takeProfitColorThe colour of a zone above the entry
stopLossColorThe colour of a zone below the entry
fillOpacityThe zone's fill opacity
strokeThickness, strokeDashArrayThe zone's outline
isEditableWhether the user can select, drag and edit it
The StopLossTakeProfitAnnotation class, with its TSDoc
/**
* @summary The {@link StopLossTakeProfitAnnotation} provides a {@link MultiPointAnnotationBase | multi-point annotation}
* which draws a stop-loss or take-profit zone over the {@link SciChartSurface}.
* @description
* To add a {@link StopLossTakeProfitAnnotation} to a {@link SciChartSurface}, use the following code:
* ```ts
* const sciChartSurface: SciChartSurface;
* const stopLossTakeProfitAnnotation = new StopLossTakeProfitAnnotation({
* points: [
* new Point(0, 5), // can also be written as "{ x: 0, y: 5 }"
* new Point(10, 7)
* ],
* strokeThickness: 1,
* strokeDashArray: [4, 2],
* takeProfitColor: "#16A34A",
* stopLossColor: "#DC2626",
* fillOpacity: 0.18
* });
* sciChartSurface.annotations.add(stopLossTakeProfitAnnotation);
* ```
* The second point chooses the take-profit or stop-loss color.
*/
class StopLossTakeProfitAnnotation extends MultiPointAnnotationBase {
readonly type: EAnnotationType;
readonly surfaceTypes: ESurfaceType[];
constructor(options?: IStopLossTakeProfitAnnotationOptions);
get strokeThickness(): number;
set strokeThickness(value: number);
get strokeDashArray(): number[];
set strokeDashArray(value: number[]);
get takeProfitColor(): string;
set takeProfitColor(value: string);
get stopLossColor(): string;
set stopLossColor(value: string);
get fillOpacity(): number;
set fillOpacity(value: number);
delete(): void;
onAttach(scs: SciChartSurface): void;
drawWithContext(renderContext: IRenderContext2D, xCalc: CoordinateCalculatorBase, yCalc: CoordinateCalculatorBase, seriesViewRect: Rect, surfaceViewRect: Rect, chartViewRect: Rect): void;
onDpiChanged(args: TDpiChangedEventArgs): void;
svgStringAdornerTemplate(): string;
toJSON(): {
type: EAnnotationType;
options: Required<Omit<IAnnotationBaseOptions, never>>;
};
}
The IStopLossTakeProfitAnnotationOptions interface, with its TSDoc
interface IStopLossTakeProfitAnnotationOptions extends IMultiPointAnnotationBaseOptions {
strokeThickness?: number;
strokeDashArray?: number[];
takeProfitColor?: string;
stopLossColor?: string;
fillOpacity?: number;
}