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@ortex-charts/financialEvents
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.