feature

onEvent and onFinish

Studies process history one collected event at a time in onEvent, then run onFinish once at the end to aggregate and publish results.

Use onEvent to filter, scope cohorts, and collect scratch values. Use onFinish for means, tables, and anything that needs the full set.

Signatures

export function onEvent(ctx: StudyCtx): void
export function onFinish(ctx: StudyCtx): void

Filtering events

export function onEvent(ctx) {
  if (ctx.event.flow !== "bull") return;
  if (ctx.event.id !== "bull_bar") return;
  // ctx.event.time, ctx.event.payload, ...
}

ctx.event.flow is the alias from flows.declare({ as }), not the slug.

scopeEvent

Call inside onEvent when results are indexed by dimensions / cohorts. Use a two-phase order for predictive studies:

  1. Scope firstctx.scopeEvent({ <scope-role dims> }) with click-known conditioning only.

  2. Outcome second — measure forward / join / classify; collect if available.

export function onEvent(ctx) {
  if (ctx.event.flow !== "bull") return;
  ctx.scopeEvent({ session: ctx.session.bucket(ctx.event.time, "london") });
  const next = measureNextSession(...);
  if (!next) return;
  ctx.collect("rets", { session: "london", nextRegime: next.regime });
}

Rules:

  • scopeEvent is onEvent-only.

  • Payload = role:"scope" dims only — never report-role outcome labels.

  • Do not gate scopeEvent on forward/outcome success. Eligibility commits at the first scopeEvent.

  • Missing data after scope commit must not invalidate chart pin eligibility.

Lifecycle checklist

  1. Declare flows / results / dimensions (with role when outcome axes are measured later).

  2. In onEvent: filter → scopeEvent (scope-role) → measure outcome → collect.

  3. In onFinish: read collected → compute → publish.

Visual accessors

const marks = ctx.flows.bull.marks(/* query */);
const atr = ctx.indicators.atr?.atr;

Flow visual accessors: events, hlines, marks, ranges, plots, trendlines, lifecycle.

Common mistakes

  • Calling publish inside onEvent (belongs in onFinish)

  • Comparing ctx.event.flow to the slug instead of the as alias

  • Putting report-role dims in scopeEvent

  • Measuring outcomes before scopeEvent (breaks live pin eligibility)

  • Forgetting that studies do not emit — missing events mean Collect / definition issues upstream

Next

  1. Collect and publish

  2. Study runtime budgets