feature
Stop / target first hit
After an OR break, asks whether price hit a +R target or −R stop first, then reports hit rates overall and by session key.
What it studies
Win / loss / neither rates for symmetric barriers around the break price, plus a table sliced by sessionKey from the event payload.
Key decisions
Piece | Why it matters |
|---|---|
| Runtime walks forward until one barrier (or neither). |
Barriers from event price × R | Risk distance comes from study params, not hard-coded ticks. |
Collect structured rows |
|
| Matches the forward barrier result shape. |
Full script
meta({
name: "OR break first hit",
kind: "study",
params: {
rTicks: { type: "number", def: 20, min: 1, max: 500 },
tickSize: { type: "number", def: 0.25, min: 0.0001 },
},
});
flows.declare({
slug: "or-break",
as: "orb",
});
results.declare({ id: "events", kind: "metric", title: "Events" });
results.declare({ id: "target_rate", kind: "metric", title: "Target rate" });
results.declare({ id: "stop_rate", kind: "metric", title: "Stop rate" });
results.declare({
id: "by_session",
kind: "table",
title: "By session",
});
export function onEvent(ctx) {
if (ctx.event.flow !== "orb") return;
const entry = Number(ctx.event.payload.close);
if (!Number.isFinite(entry)) return;
const dist = ctx.params.rTicks * ctx.params.tickSize;
const isUp = ctx.event.id === "or_break_up";
const targetAt = isUp ? entry + dist : entry - dist;
const stopAt = isUp ? entry - dist : entry + dist;
const res = ctx.candles.forward({
targetAt,
stopAt,
firstHit: true,
});
if (!res) return;
ctx.collect("rows", {
sessionKey: ctx.event.payload.sessionKey || "unknown",
hit: res.hit,
side: isUp ? "up" : "dn",
});
}
export function onFinish(ctx) {
const rows = ctx.collected("rows");
const n = rows.length;
const targets = rows.filter((r) => r.hit === "target").length;
const stops = rows.filter((r) => r.hit === "stop").length;
ctx.publish("events", { value: n });
ctx.publish("target_rate", {
value: n ? targets / n : 0,
format: "percent",
});
ctx.publish("stop_rate", {
value: n ? stops / n : 0,
format: "percent",
});
const keys = [];
for (const r of rows) {
if (!keys.includes(r.sessionKey)) keys.push(r.sessionKey);
}
ctx.publish(
"by_session",
keys.map((sessionKey) => {
const slice = rows.filter((r) => r.sessionKey === sessionKey);
const sn = slice.length;
const st = slice.filter((r) => r.hit === "target").length;
return {
sessionKey,
events: sn,
target_rate_pct: sn ? Number(((st / sn) * 100).toFixed(1)) : 0,
};
})
);
}Showing these numbers on the Terminal uses Put on chart / the Forward insight agent — not the study authoring agent. See Show studies on charts.
