First definition guide
This walkthrough builds a small definition by hand: it marks every bar that closes above its open, with a label on each mark, ready to run over history and measure with a study.
In the API a definition is a flow: kind: "flow", flows.declare, imports.use({ flowId }). Both words mean the same thing. To have the agent write a definition for you instead, follow Test your first trading idea.

Steps
Open Tools → Definitions and click New.
Click Build it yourself under the suggestions and give the definition a Name.
Paste the script below into the Code tab and click Save draft.
Open the Chart tab and check the marks. The text on each mark is the emit's
label, not the event id.Run the definition over history to collect its events, then build a study on them.
Problems lists what lint flagged, Errors what stopped the script from running, and Logs what it printed with ctx.log.
Full script
meta({
name: "Bull bar",
kind: "flow",
slug: "bull-bar",
shortName: "BullBar",
});
events.declare({
id: "bull_bar",
intent: "signal",
payload: {
range: { type: "number", description: "High minus low of the bar, in price units" },
},
paint: "pin",
});
outputs.declare({
id: "close_line",
kind: "line",
name: "Close",
color: "#64b5f6",
});
export function onBar(ctx) {
ctx.plot("close_line", ctx.close);
if (ctx.close <= ctx.open) return;
const range = ctx.high - ctx.low;
ctx.emit(
"bull_bar",
{ range },
{
label: `Bull · R=${range.toFixed(2)}`,
subtitle: `close ${ctx.close.toFixed(2)}`,
price: ctx.close,
}
);
}What each part does
Piece | Job |
|---|---|
| Lets the script emit events. |
| The stable name studies and other scripts use to depend on this one. Renaming it breaks every consumer |
| The event's id, a short |
| How an emit shows on the chart: |
| A series the definition draws, fed by |
| Records one event. |
timeStart and timeEnd in opts set the event's time range and both default to ctx.time. A point-in-time event needs neither.
Rules that bite early
Every
ctx.emitneeds a non-emptylabel. Lint warns withevents/emit-missing-label, and a run drops the emit.Payload keys
_displayLabeland_displaySubtitleare reserved. Pass that text aslabelandsubtitleinstead.To fire at most once per day, session or level, declare
oncePer: { keys: [...] }on the event, naming payload fields. The runtime keeps one emit per unique combination of those fields over the whole run. Never build your own dedupe set inctx.accum.paint: "highlight_price"and"highlight_event_bar_and_price"draw at a price. Declarepayload.priceas a number, or lint warns withevents/paint-needs-price.opts.priceoverrides it on the chart only; a run over history ignores it.paint: "none"hides the events on the chart. Lint warns withevents/hidden-paint-explicit.The entry file uses
export function onBar(ctx). Helpers in other files use relative imports such asimport { isBull } from "./lib/bars.js". See Multi-file packages and imports.
Emit and labels and Events and snapshots cover every option.
