# Markers and events

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

Section: Charts. Source: https://ortexcharts.com/docs/markers

## 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

```ts
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](https://ortexcharts.com/docs/replay)
  does not leak the future.

```ts
const markers = chart.setMarkers(list);
markers.getMarkers();          // the normalized list, ids filled in
```

_Live example: [stock-chart](https://ortexcharts.com/examples/stock-chart)_

## Events

```ts
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

```ts
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](https://ortexcharts.com/docs/drawings) and [alerts](https://ortexcharts.com/docs/replay).

```ts
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.

```ts
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.
