Skip to content

Charts

Markers and events

Earnings, dividends, filings and news on the chart, as badges, lanes, callouts and ranges.

Two Layers, Different Jobs

There are two ways to put something on the chart at a point in time, and they are not interchangeable.

  • Markers (setMarkers) are trade-style marks anchored to a bar and a price: arrows, flags, circles, labels. They stack outward on a shared bar and collapse into a count when the zoom gets tight. This is the layer for entries, exits and signals.
  • Events (setEvents) are annotations anchored to time, in five presentation styles: lane badges above the time axis, badges next to the bar, callouts with a leader line, vertical lines and shaded ranges. This is the layer for earnings, dividends, filings, lock-ups and news.

Both are primitives, both carry a tooltip and an arbitrary payload, and both surface through chart.subscribeClick. Use markers for things that belong to a price and events for things that belong to a moment.

Markers

chart.setMarkers([
  {
    time: bars[120].time,
    position: "belowBar",
    shape: "arrowUp",
    color: "#22C08A",
    text: "B",
    tooltip: ["Bought 400 at 182.40", "Order 88213"],
    data: { orderId: 88213 },
  },
  { time: bars[164].time, position: "aboveBar", shape: "arrowDown", color: "#EF4E5A", text: "S" },
]);

setMarkers replaces the whole list and returns the primitive, so calling it again with a new array is the normal way to update. The primitive is created on the first call and reused after that.

Field Type Notes
id string Generated when omitted.
time number Milliseconds since the epoch; snapped to the nearest bar.
position "aboveBar" | "belowBar" | "inBar" | "top" | "bottom" Relative to the bar, or pinned to the top or bottom of the pane.
shape "arrowUp" | "arrowDown" | "circle" | "square" | "diamond" | "flag" | "label" label draws text in a rounded box.
color string Falls back to the theme text color.
text string One or two characters inside or beside the mark.
tooltip string | string[] Shown on hover; an array becomes lines.
size number Overrides the size derived from the bar spacing.
data unknown Returned with click and hover events.

Behavior worth knowing:

  • Markers on the same bar stack outward, so a bar with an entry and an exit shows both.
  • Markers that share a pixel column at low zoom collapse into a count badge, so a decade of trades does not become a solid stripe.
  • Markers past the last bar on the timeline are not drawn, so bar replay does not leak the future.
const markers = chart.setMarkers(list);
markers.getMarkers();          // the normalized list, ids filled in
Stock chart@ortex-charts/financial
Loading stock chart
Ten years of IBM daily bars with a moving average pair, volume, earnings markers and a crosshair readout.

Open this example with its source

Events

chart.setEvents([
  { time: earnings, style: "lane", group: "Earnings", text: "E", tooltip: ["Q3 earnings", "EPS 2.18 vs 2.04 est"] },
  { time: dividend, style: "lane", group: "Dividends", text: "D", tooltip: "Ex-dividend 0.25" },
  { time: fomc, style: "line", text: "FOMC", lineStyle: "dashed", color: "#60A5FA" },
  { time: newsAt, style: "callout", text: "Guidance raised", position: "aboveBar" },
  { time: lockupStart, endTime: lockupEnd, style: "range", text: "Lock-up", color: "#A78BFA" },
]);

The Five Styles

Style Looks like Used for
lane A letter badge in a strip above the time axis, one row per group. Earnings, dividends, splits — the convention traders already read.
badge A letter in a circle, square or pin next to the bar. Signals, alerts, pinned news.
callout A text box on the chart with a leader line to the bar. Analyst calls, headlines, trade notes.
line A vertical line across the pane with a label tag. Macro events, index rebalances, product launches.
range A shaded band between time and endTime with a label. Lock-ups, blackout periods, trading halts.

Lanes never overlap the price, which is why they are the right default for a recurring corporate-action feed. Badges and lane items collapse into a count at low zoom; callouts truncate and stack so they do not cover each other.

The Event Shape

Field Type Notes
id string Generated when omitted.
time number Anchor, in milliseconds.
endTime number Range end; only for range.
style EventStyle One of the five above.
text string One or two letters for badges and lanes; the label for the rest.
tooltip string | string[] Hover text.
color string Falls back to the theme.
position "aboveBar" | "belowBar" | "top" | "bottom" For badges and callouts.
price number Callout anchor price; defaults to the bar high or low.
shape "circle" | "square" | "pin" Badge shape.
group string Lane row key; badges of one group share a row.
lineStyle "solid" | "dashed" | "dotted" For line events.
data unknown Returned with click and hover events.

Layout Options

import { addEvents } from "@ortex-charts/financial";

const events = addEvents(chart.chart, chart.main, list, {
  laneHeight: 18,
  badgeSize: 16,
  rangeOpacity: 0.12,
});

chart.setEvents(list) is the same thing with the defaults. Use addEvents directly when you want the options or when the events belong to a series other than the main one.

Reacting to Clicks and Hovers

Both layers report through the chart hit test, so one subscription covers everything on the chart, including drawings and alerts.

chart.chart.subscribeClick((e) => {
  if (!e.hit) return;
  const payload = e.hit.data as { orderId?: number } | undefined;
  if (payload?.orderId) openOrder(payload.orderId);
});

chart.chart.subscribeCrosshairMove((e) => {
  setHovered(e.hit?.id ?? null);
});

The tooltip in HitResult.tooltip is rendered by the chart itself, so a marker or event with a tooltip needs no work from you. Subscribe only when you want behavior beyond the tooltip.

A Worked Example: A Corporate-Action Feed

The usual integration is a list of dated records from an API turned into lanes, one row per family, with the raw record kept for the detail panel.

type Action = { date: string; kind: "earnings" | "dividend" | "split"; headline: string; detail: string };

const LETTER = { earnings: "E", dividend: "D", split: "S" } as const;
const GROUP = { earnings: "Earnings", dividend: "Dividends", split: "Splits" } as const;

chart.setEvents(
  actions.map((a) => ({
    time: Date.parse(`${a.date}T00:00:00Z`),
    style: "lane" as const,
    group: GROUP[a.kind],
    text: LETTER[a.kind],
    tooltip: [a.headline, a.detail],
    data: a,
  })),
);

chart.chart.subscribeClick((e) => {
  const action = e.hit?.data as Action | undefined;
  if (action?.date) showActionDetail(action);
});

Events do not need to be sorted; the primitive sorts and normalizes the list. Re-calling setEvents on a symbol change replaces everything, which is the intended pattern when the chart changes symbol.