Parabolic SAR (J. Welles Wilder Jr., 1978) — the canonical multi-column
state machine, and kernel K6's first consumer.
A stop that trails the trend and accelerates towards it, reversing side
whenever price crosses it:
SAR[i+1] =SAR[i] +AF × (EP − SAR[i])
where EP is the extreme point of the current leg (the highest high while
long, the lowest low while short) and AF starts at step, grows by
step on each new extreme, and is capped at maxStep. When price
penetrates the stop the side reverses, SAR is overridden with the leg's
EP, and AF resets. The advance is clamped so a printed stop never sits
inside the last two bars' ranges.
Reads high and low only — the close plays no part in Wilder's rule.
Two columns, and why the value one is bare ${prefix}
${prefix} — the stop value (psar at the default prefix).
${prefix}Trend — +1 while the stop trails below price, −1 above.
The side is the half consumers need and cannot reliably recover: a chart
draws the dot below the bar when long and above it when short, and
psar < low is not a safe derivation because the clamp can print a
stop exactly on an extreme (the advance is clamped to min(sar, prevLow, low), so equality is reachable, and then a strict comparison flips the
dot to the wrong side). So the flag is carried out of the machine rather
than re-derived from its output.
Two columns means a prefix, per the studies README's uniform shape. The
value column is the bare prefix rather than ${prefix}Line, which is a
departure from macd's ${prefix}Line/Signal/Hist, and
deliberate: MACD's three columns are three peers with no principal among
them, whereas Parabolic SAR is one number and the trend is an annotation
on it. Naming the value psarLine would make this the one study whose
principal output is not reachable under the name the caller passed, and
psar() would leave no column called psar. The precedent it follows is
the single-output studies' bare output (atr, obv), not
MACD's.
Which SAR — TA-Lib, bar for bar
TA-Lib's SAR(high, low, acceleration, maximum) is the reference and this
matches it exactly (0.0 maximum absolute difference across
(0.02, 0.2), (0.05, 0.5) and (0.01, 0.1) on the oracle's 80 bars,
asserted in the generator). Three details of Wilder's rule are ambiguous in
prose and are pinned to TA-Lib's reading:
The initial side comes from Wilder's −DM over the first one-bar
move: short if the low fell further than the high rose, long otherwise
(including on a tie and on an inside bar). Measured, not recalled.
The first printed bar is bar 1, and TA-Lib treats "yesterday" as
"today" for its clamp on that bar only.
The clamp uses the last two bars, both on a reversal override and on
an ordinary advance.
SAREXT (which adds offset-on-reverse and independent long/short
acceleration parameters) is a different function with a different output
convention — it signs the short-side output negative — and is out of scope.
Warm-up and gaps
Length-preserving: bar 0 is undefined on both columns (the seed needs a
predecessor) and every bar from 1 on carries a value, matching TA-Lib's
look-back of 1.
A missing high or lowresets the machine ([PND-SFOLD]): that bar is
undefined on both columns, and the next two complete bars re-seed the
side from scratch. That is a deliberate delta from TA-Lib, which does not
check for NaN at all and whose comparisons against one are all false —
measured on an interior hole, TA-Lib emits a full column of numbers with
no gap in it. The reasoning for resetting is on kernels/fold.ts: a SAR
that did not see a bar cannot know whether it flipped, and a wrong side
never washes out.
Edges
In the units of the price: scaling every bar by k scales the stop
by k and leaves the trend unchanged; adding c shifts it by c. It
does not normalise (contrast rsi).
step and maxStep are rates, not bar counts, so they are validated
as positive finite numbers rather than by assertPeriod. maxStep below
step would cap the factor before its first increment; that is rejected.
A flat bar (high === low) is ordinary input — the machine reads
both, and neither a division nor a degenerate window arises anywhere in
the rule, so there is no zero-denominator case to guard.
Parabolic SAR (J. Welles Wilder Jr., 1978) — the canonical multi-column state machine, and kernel K6's first consumer.
A stop that trails the trend and accelerates towards it, reversing side whenever price crosses it:
where
EPis the extreme point of the current leg (the highest high while long, the lowest low while short) andAFstarts atstep, grows bystepon each new extreme, and is capped atmaxStep. When price penetrates the stop the side reverses,SARis overridden with the leg'sEP, andAFresets. The advance is clamped so a printed stop never sits inside the last two bars' ranges.Reads high and low only — the close plays no part in Wilder's rule.
Two columns, and why the value one is bare
${prefix}${prefix}— the stop value (psarat the default prefix).${prefix}Trend—+1while the stop trails below price,−1above.The side is the half consumers need and cannot reliably recover: a chart draws the dot below the bar when long and above it when short, and
psar < lowis not a safe derivation because the clamp can print a stop exactly on an extreme (the advance is clamped tomin(sar, prevLow, low), so equality is reachable, and then a strict comparison flips the dot to the wrong side). So the flag is carried out of the machine rather than re-derived from its output.Two columns means a
prefix, per the studies README's uniform shape. The value column is the bare prefix rather than${prefix}Line, which is a departure from macd's${prefix}Line/Signal/Hist, and deliberate: MACD's three columns are three peers with no principal among them, whereas Parabolic SAR is one number and the trend is an annotation on it. Naming the valuepsarLinewould make this the one study whose principal output is not reachable under the name the caller passed, andpsar()would leave no column calledpsar. The precedent it follows is the single-output studies' bareoutput(atr, obv), not MACD's.Which SAR — TA-Lib, bar for bar
TA-Lib's
SAR(high, low, acceleration, maximum)is the reference and this matches it exactly (0.0maximum absolute difference across(0.02, 0.2),(0.05, 0.5)and(0.01, 0.1)on the oracle's 80 bars, asserted in the generator). Three details of Wilder's rule are ambiguous in prose and are pinned to TA-Lib's reading:−DMover the first one-bar move: short if the low fell further than the high rose, long otherwise (including on a tie and on an inside bar). Measured, not recalled.SAREXT(which adds offset-on-reverse and independent long/short acceleration parameters) is a different function with a different output convention — it signs the short-side output negative — and is out of scope.Warm-up and gaps
Length-preserving: bar 0 is
undefinedon both columns (the seed needs a predecessor) and every bar from 1 on carries a value, matching TA-Lib's look-back of 1.A missing
highorlowresets the machine ([PND-SFOLD]): that bar isundefinedon both columns, and the next two complete bars re-seed the side from scratch. That is a deliberate delta from TA-Lib, which does not check forNaNat all and whose comparisons against one are all false — measured on an interior hole, TA-Lib emits a full column of numbers with no gap in it. The reasoning for resetting is onkernels/fold.ts: a SAR that did not see a bar cannot know whether it flipped, and a wrong side never washes out.Edges
kscales the stop bykand leaves the trend unchanged; addingcshifts it byc. It does not normalise (contrast rsi).stepandmaxStepare rates, not bar counts, so they are validated as positive finite numbers rather than byassertPeriod.maxStepbelowstepwould cap the factor before its first increment; that is rejected.high === low) is ordinary input — the machine reads both, and neither a division nor a degenerate window arises anywhere in the rule, so there is no zero-denominator case to guard.