ChartnautDocs

Event count

Counts how often the Bull bar definition fired, and on which weekdays. It is the smallest complete study: one gate in scope, one collect, three results.

What it measures

Every bull_bar event in the run, grouped by UTC weekday, with the average bar range for each day.

Key decisions

Piece

Why

flows.declare({ slug, as, version })

Binds the definition's recorded events. as is what ctx.event.flow holds. version is the one you ran over history.

scope returns {} or null

Every event study exports scope. No dimensions, so a bull bar returns an empty object and any other event returns null.

ctx.collect in onEvent, ctx.publish in onFinish

Store one row per event, aggregate once all events are in.

Table by weekday

At most seven rows. A table with a row per event is too long to read.

readout__summary and readout__events

A written answer and a headline count, which lint expects beside a table.

Full script

meta({ name: "Bull bar count", kind: "study" });

flows.declare({ slug: "bull-bar", as: "bull", version: 2 });

results.declare({
  id: "readout__summary",
  kind: "prose",
  role: "answer",
  title: "Summary",
  description: "How often bull bars fired and on which weekday most often.",
});

results.declare({
  id: "readout__events",
  kind: "metric",
  title: "Bull bars",
  description: "Bull bar events in the run window.",
});

results.declare({
  id: "events_by_weekday",
  kind: "table",
  title: "Bull bars by weekday",
  description: "Event count and average bar range (high minus low) for each UTC weekday.",
});

export function scope(ctx) {
  if (ctx.event.flow !== "bull") return null;
  return {};
}

export function onEvent(ctx, scope) {
  ctx.collect("rows", {
    weekday: ctx.session.bucket(ctx.event.time, "dow"),
    range: ctx.event.payload.range,
  });
  ctx.mark(ctx.event.time, ctx.event.payload.close, { color: "#26a69a", shape: "circle" });
}

export function onFinish(ctx) {
  const rows = ctx.collected("rows");
  const counts = ctx.bin.byKey(rows, (r) => r.weekday, (r) => r.range, "count");
  const avgRange = new Map(ctx.bin.byKey(rows, (r) => r.weekday, (r) => r.range, "mean"));

  const table = counts.map(([weekday, events]) => ({
    weekday,
    events,
    avg_range: Number(avgRange.get(weekday).toFixed(2)),
  }));
  ctx.publish("events_by_weekday", table);

  ctx.publish("readout__events", { value: rows.length, format: "integer" });

  const busiest = table.reduce((best, r) => (r.events > best.events ? r : best), table[0]);
  ctx.publish("readout__summary", {
    body: rows.length
      ? `${rows.length} bull bars. ${busiest.weekday} had the most, with ${busiest.events}.`
      : "No bull bars in this run.",
    role: "answer",
  });
}

This study only counts, so Attach to chart does not offer it. Forward return measures what came next.