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.

Next: 106 — ADX / DMI (trend strength)