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): voidFiltering 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:
Scope first —
ctx.scopeEvent({ <scope-role dims> })with click-known conditioning only.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:
scopeEventis onEvent-only.Payload =
role:"scope"dims only — never report-role outcome labels.Do not gate
scopeEventon forward/outcome success. Eligibility commits at the firstscopeEvent.Missing data after scope commit must not invalidate chart pin eligibility.
Lifecycle checklist
Declare flows / results / dimensions (with
rolewhen outcome axes are measured later).In
onEvent: filter →scopeEvent(scope-role) → measure outcome →collect.In
onFinish: readcollected→ 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
publishinsideonEvent(belongs inonFinish)Comparing
ctx.event.flowto the slug instead of theasaliasPutting report-role dims in
scopeEventMeasuring outcomes before
scopeEvent(breaks live pin eligibility)Forgetting that studies do not emit — missing events mean Collect / definition issues upstream
