The move is decomposed, not thresholded
Each stock is measured against a behavioural peer cluster where one exists, and a beta-adjusted Nifty where it does not. What survives the peer move is the residual, scored in units of that stock's own residual volatility — so the bar is set by how quietly it usually trades, not by a number someone picked.
Every signal names its mechanism
EXCESS_MOVE, VOLUME_CONFIRMED, GAP, LEVEL_BREAK, SINCE_SEEN_MOVE. Each carries the numbers it fired on: a gap clears 2%, volume confirmation needs 1.5× the stock's normal, a move since you last looked has to clear 1.5% and twice the drift expected over that gap. You read why, not just that.
Rules in English, compiled to something you can audit
Describe a trigger — "drops more than 2% against its peers on 3x volume" — and it compiles to a bounded JSON condition you read and confirm before it saves. You can open the compiled rule. The model writes it; the engine, never the model, evaluates it.
What it suppressed is on the record
The evidence page replays all three alert rules over the same stored bars and breaks the gap down: market-wide, below the floor, or inside the stock's own noise. It also lists the six days a percentage rule misses — raw moves under 1.8% whose peer-adjusted residual was over 2.8%.