Stochastic
Lane’s stochastic on the true high-low range: where the close sits inside the window’s range,
smoothed once for %K and again for %D. The seeded StochasticClose computes the same thing on
closes only; this is the textbook form.
Signature
Stochastic(period, smooth_k, smooth_d, on=<source>)| Argument | Type | Required | Notes |
|---|---|---|---|
period |
int Param or literal | Yes | Range lookback. Default 14, bounds 1–400. |
smooth_k |
int Param or literal | Yes | SMA applied to raw %K. 1 gives fast %K; 3 the slow form. Bounds 1–50. |
smooth_d |
int Param or literal | Yes | SMA applied to %K to make %D. Default 3, bounds 1–50. |
on= |
symbol | No | Source symbol. Defaults to the trade symbol. Reads high, low, close. |
Formula
$$ %K_{raw} = 100,\frac{C - \min(L, n)}{\max(H, n) - \min(L, n)}, \quad %K = \text{SMA}(%K_{raw}, s_k), \quad %D = \text{SMA}(%K, s_d) $$
Warmup
period + smooth_k + smooth_d bars to full accuracy; during the lead-in the smoothing windows
are partial. A perfectly flat window returns 50 (neutral), not 0.
Reading the value
st.kst.dst.k < 20Multi-value: cannot be chained onto or crossed; compare st.k and st.d with > / <.
Example
class StochFade(Strategy): """Fade oversold: buy %K under 20 turning up through %D.""" period = Param(14, min=1, max=400) trade = Symbol() sizing = FixedCapital()
def execute(self): st = Stochastic(self.period, 3, 3, on=self.trade) if st.k < 20 and st.k > st.d and self.trade.is_flat: self.buy(self.trade, reason=f"K {st.k:.1f} turning up") if st.k > 80 and self.trade.is_long: self.sell(self.trade, self.trade.position, reason="Overbought")Notes
- George Lane’s %K/%D; the slow form (
smooth_k=3) is what most charts label “Stochastic”. - Proven by an assert-via-trade run on SPY daily (%K and %D matched a strategy-side reference to 0.01 on every bar; zero alarm trades; canary fired).
- Flat-window sentinel is 50; a 0 would read as maximally oversold.
smooth_kdefaults to 3 here, the slow stochastic. Passsmooth_k=1for the raw, fast %K.