DEV Community

Timevolt
Timevolt

Posted on

Moving Averages and RSI: The Jedi's Guide to Market Trends

The Quest Begins (The "Why")

Honestly, I used to stare at candlestick charts feeling like I was trying to read ancient runes. Every time I thought I spotted a trend, the price would zig‑zag and I’d second‑guess myself. I’d open a notebook, scribble a few closing prices, and manually calculate a simple moving average (SMA) just to see if the market was “breathing” in a rhythm. After a few hours of that, my brain felt like it had been through a lightsaber duel—exhausted and bruised.

That’s when I realized I needed a spell (or at least a reliable script) to automate the grunt work so I could focus on the story the price was telling. Enter moving averages and the Relative Strength Index (RSI)—two classic technical‑analysis tools that, when combined, act like a Jedi’s senses: they smooth out the noise and feel the Force of momentum.

The Revelation (The Insight)

The magic isn’t in the formulas themselves; it’s in how they filter and amplify price data.

  • Moving Average (MA) – takes a window of recent prices and outputs their average. Short windows (e.g., 10‑day) react quickly; long windows (e.g., 50‑day) show the underlying trend. When the short MA crosses above the long MA, many traders read it as a bullish signal (the classic “golden cross”).
  • Relative Strength Index (RSI) – measures the speed and change of price movements on a scale of 0‑100. Values above 70 hint at overbought conditions (maybe a pullback), while below 30 hint at oversold (maybe a bounce).

The real “aha!” moment came when I saw these two indicators talk to each other: a bullish MA crossover plus an RSI climbing out of oversold territory often precedes a strong upward move. It’s like having both a lightsaber and a Force push—you’re not just swinging blindly; you’re guided.

Wielding the Power (Code & Examples)

Below is a quick Python walkthrough using pandas. I’ll first show the struggle (manual loops) and then the victory (vectorized, reusable functions).

The Struggle – Manual Loops

# Imagine we have a DataFrame `df` with a 'close' column
def manual_sma(series, window):
    sma = []
    for i in range(len(series)):
        if i < window - 1:
            sma.append(None)          # not enough data yet
        else:
            window_slice = series[i - window + 1:i + 1]
            sma.append(sum(window_slice) / window)
    return sma

def manual_rsi(series, period=14):
    # Calculate gains/losses
    delta = series.diff()
    gain = delta.where(delta > 0, 0)
    loss = -delta.where(delta < 0, 0)

    # First average gain/loss
    avg_gain = gain.rolling(window=period).mean()
    avg_loss = loss.rolling(window=period).mean()

    rs = avg_gain / avg_loss
    rsi = 100 - (100 / (1 + rs))
    return rsi

# Using the functions (slow on large data)
df['sma_10'] = manual_sma(df['close'], 10)
df['sma_50'] = manual_sma(df['close'], 50)
df['rsi']    = manual_rsi(df['close'])
Enter fullscreen mode Exit fullscreen mode

Pros: Easy to follow line‑by‑line.

Cons: Loops over every row → painfully slow on anything beyond a few thousand candles. Plus, handling the first window‑1 NaNs is messy.

The Victory – Vectorized Pandas

import pandas as pd

def add_technicals(df, short_window=10, long_window=50, rsi_period=14):
    # ----- Moving Averages -----
    df[f'sma_{short_window}'] = df['close'].rolling(window=short_window).mean()
    df[f'sma_{long_window}']  = df['close'].rolling(window=long_window).mean()

    # ----- RSI -----
    delta = df['close'].diff()
    gain = delta.clip(lower=0)
    loss = -delta.clip(upper=0)

    avg_gain = gain.rolling(window=rsi_period).mean()
    avg_loss = loss.rolling(window=rsi_period).mean()

    rs = avg_gain / avg_loss
    df['rsi'] = 100 - (100 / (1 + rs))

    # Optional: signal columns
    df['golden_cross'] = (df[f'sma_{short_window}'] > df[f'sma_{long_window}']) & \
                         (df[f'sma_{short_window}'].shift(1) <= df[f'sma_{long_window}'].shift(1))
    df['rsi_oversold'] = df['rsi'] < 30
    df['rsi_overbought'] = df['rsi'] > 70

    return df

# Usage
df = add_technicals(df.copy())
print(df.tail())
Enter fullscreen mode Exit fullscreen mode

Why this feels like leveling up:

  • One call to rolling().mean() does the SMA for all rows in C‑speed.
  • The RSI calculation stays fully vectorized—no explicit loops, no manual NaN handling.
  • We tack on useful boolean flags (golden_cross, rsi_oversold, rsi_overbought) that let us spot the Jedi‑style combo in a single line:
signal = df['golden_cross'] & df['rsi_oversold']
Enter fullscreen mode Exit fullscreen mode

Common Traps (The “Bosses” to Avoid)

  1. Using .mean() on the whole series instead of a rolling window – you’ll get a single number (the overall average) and think your MA is flat.
  2. Forcing the RSI calculation before the first period rows have enough data – you’ll end up with division‑by‑zero or infinite values. The rolling().mean() naturally returns NaN until the window fills, which is exactly what we want.

Avoid those, and your indicators will behave like a well‑trained Padawan—predictable and reliable.

Why This New Power Matters

Now that you can slap SMAs and RSI onto any price series in a few lines, you’re free to:

  • Backtest strategies across years of data in seconds instead of hours.
  • Combine the signals with other indicators (MACD, Bollinger Bands) to build a personalized trading “lightsaber”.
  • Visualize the crossover and RSI zones directly on your charts, making the story of the market instantly readable.

The best part? You’re not just copying someone else’s script; you understood the underlying math, so you can tweak windows, periods, or even create hybrid indicators that fit your own style. That’s the real power—turning raw numbers into intuition.

Your Turn – The Challenge

Grab any public OHLCV dataset (Yahoo Finance, Binance, or even a CSV you’ve got lying around). Use the add_technicals function above, then experiment:

  • Try different SMA windows (20/100, 5/20) and see how the crossover frequency changes.
  • Plot the price with sma_20, sma_50, and shade areas where rsi < 30 (oversold) or rsi > 70 (overbought).
  • Write a simple rule: “Buy when golden_cross AND rsi_oversold, Sell when rsi_overbought.”

Did the equity curve improve? What tweaks made the biggest difference? Drop your findings in the comments—I’m genuinely curious to hear what you discover!

May the Force (and good indicators) be with you on your next trading quest. 🚀

Top comments (0)