Stochastic Oscillator
Measures where the current close sits relative to the recent high-low range — a different lens on "overbought/oversold" than RSI's gain/loss-based approach (chapter 103).
def stochastic(df: pd.DataFrame, k_period: int = 14, d_period: int = 3, smooth_k: int = 3) -> pd.DataFrame:
low_min = df["low"].rolling(k_period).min()
high_max = df["high"].rolling(k_period).max()
raw_k = 100 * (df["close"] - low_min) / (high_max - low_min)
k = raw_k.rolling(smooth_k).mean() # "slow" %K — smoothed, standard in most platforms
d = k.rolling(d_period).mean() # %D — signal line, smoothed %K
return pd.DataFrame({"k": k, "d": d}, index=df.index)
%K vs %D — same crossover logic as MACD's line/signal (chapter 102)
def stochastic_crossover_signal(stoch_df: pd.DataFrame) -> pd.Series:
signal = pd.Series(0, index=stoch_df.index)
bullish = (stoch_df["k"] > stoch_df["d"]) & (stoch_df["k"].shift(1) <= stoch_df["d"].shift(1))
bearish = (stoch_df["k"] < stoch_df["d"]) & (stoch_df["k"].shift(1) >= stoch_df["d"].shift(1))
signal[bullish] = 1
signal[bearish] = -1
return signal
Overbought/oversold zones — conventionally 80/20
def stochastic_zone_signal(stoch_df: pd.DataFrame, overbought=80, oversold=20) -> pd.Series:
signal = pd.Series(0, index=stoch_df.index)
signal[stoch_df["k"] < oversold] = 1
signal[stoch_df["k"] > overbought] = -1
return signal
Same caveat as RSI (chapter 103) applies: these zones are conventions, not guarantees — a stochastic pinned near 100 in a strong uptrend is telling you the trend is strong, not that a reversal is imminent.
Why Stochastic and RSI aren't redundant despite looking similar
RSI is based on the *magnitude* of gains vs losses over the period; Stochastic is based purely on *where close sits within the high-low range*. Two instruments/periods can show meaningfully different readings between the two — a bar that closes at its high after a wide range but on relatively small net gain can show a high Stochastic %K with a much less extreme RSI. Using both together (part of chapter 114's confluence scoring) captures information neither alone fully represents.
Fast vs slow Stochastic
The formula above computes the standard "slow" version (smooth_k=3) used by most platforms by default. Setting smooth_k=1 gives the "fast" (raw, noisier) version — fast stochastic reacts quicker but generates substantially more false signals; slow is the more common default for a reason.