Skip to content
Open RaamTrade

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.k
st.d
st.k < 20

Multi-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_k defaults to 3 here, the slow stochastic. Pass smooth_k=1 for the raw, fast %K.