DEV Community

Timevolt
Timevolt

Posted on

Moving Averages & RSI: The Matrix of Trading Signals

The Quest Begins (The "Why")

Honestly, I was stuck in a loop. I’d just finished scraping daily OHLCV data for a handful of altcoins and thought, “Let’s see if a simple buy‑low‑sell‑high rule works.” I slapped together a script that bought whenever today’s close was higher than yesterday’s and sold otherwise. The equity curve looked like a drunkard’s walk—up, down, sideways, and more noise than signal. I felt like Neo before he took the red pill: aware that something was off, but unable to see the underlying pattern.

The problem wasn’t my data; it was my lens. Price series are noisy, and reacting to every tick is like trying to dodge every bullet in a firefight—you’ll get hit sooner or later. I needed a way to smooth out the chaos and gauge whether the market was over‑extended or ready to bounce. That’s when I dove into moving averages and the Relative Strength Index (RSI). Turns out, these two indicators are the “bullet‑time” of technical analysis: they slow things down just enough to spot the real moves.

The Revelation (The Insight)

Moving Averages – the Trend’s Whisper

A moving average (MA) is just the average price over a look‑back window, but that simple act does two powerful things:

  1. Smoothing – it filters out high‑frequency noise, revealing the underlying direction.
  2. Lag – because it’s based on past prices, it reacts slower than the raw signal, which can be a blessing (fewer whipsaws) or a curse (late entries).

The two flavors I use most are the Simple Moving Average (SMA) and the Exponential Moving Average (EMA). SMA treats every point in the window equally; EMA gives more weight to recent prices, making it hug the price tighter when the trend accelerates. Think of SMA as the steady hobbit walking the Shire, and EMA as the spry elf who darts ahead when the path clears.

RSI – the Momentum Meter

The Relative Strength Index oscillates between 0 and 100 and measures the speed and magnitude of price changes. The classic formula:

RS = (Average Gain over N periods) / (Average Loss over N periods)
RSI = 100 - (100 / (1 + RS))
Enter fullscreen mode Exit fullscreen mode

When RSI climbs above 70, the asset is considered overbought—price may be due for a pull‑back. Below 30 signals oversold—a potential bounce. The magic isn’t in the exact thresholds; it’s in spotting divergences. If price makes a new high while RSI fails to do the same, the upward momentum is weakening—a classic warning sign.

Together, SMA/EMA give you the “where is the market heading?” answer, while RSI tells you “how strong is that move right now?” It’s like having a map and a compass on the same quest.

Wielding the Power (Code & Examples)

Let’s see these ideas in action with Python and pandas. I’ll grab Bitcoin daily data, compute a 20‑day EMA and a 14‑day RSI, then generate a naive signal: buy when price crosses above EMA and RSI < 30 (oversold bounce), sell when price crosses below EMA or RSI > 70 (overbought exhaustion).

The Struggle – Raw Price Only

import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt

# Grab data
df = yf.download("BTC-USD", start="2022-01-01", end="2024-01-01")
df = df[['Close']].copy()
df['Signal'] = 0
df.loc[df['Close'].diff() > 0, 'Signal'] = 1   # buy if price up
df.loc[df['Close'].diff() < 0, 'Signal'] = -1  # sell if price down

# Plot raw equity curve (simple buy‑and‑hold of signal)
df['Returns'] = df['Close'].pct_change()
df['Strategy'] = df['Signal'].shift(1) * df['Returns']
df['Equity'] = (1 + df['Strategy']).cumprod()

plt.figure(figsize=(10,4))
plt.plot(df['Equity'], label='Raw Signal Equity')
plt.title('Equity Curve – Price‑Only Signal')
plt.legend()
plt.show()
Enter fullscreen mode Exit fullscreen mode

The equity curve jitters wildly—lots of false triggers, tiny gains eaten by transaction costs.

The Victory – Adding EMA & RSI

import ta  # pip install ta

# EMA (20‑day)
df['EMA20'] = ta.trend.ema_indicator(df['Close'], window=20)

# RSI (14‑day)
df['RSI14'] = ta.momentum.rsi(df['Close'], window=14)

# Generate signals
df['Signal'] = 0
# Buy: price crosses above EMA20 AND RSI < 30 (oversold)
buy_cond = (df['Close'] > df['EMA20']) & (df['Close'].shift(1) <= df['EMA20'].shift(1)) & (df['RSI14'] < 30)
# Sell: price crosses below EMA20 OR RSI > 70 (overbought)
sell_cond = (df['Close'] < df['EMA20']) & (df['Close'].shift(1) >= df['EMA20'].shift(1)) | (df['RSI14'] > 70)

df.loc[buy_cond, 'Signal'] = 1
df.loc[sell_cond, 'Signal'] = -1

# Compute strategy returns
df['Returns'] = df['Close'].pct_change()
df['Strategy'] = df['Signal'].shift(1) * df['Returns']
df['Equity'] = (1 + df['Strategy']).cumprod()

# Plot
plt.figure(figsize=(12,6))
plt.plot(df['Equity'], label='EMA+RSI Strategy')
plt.plot(df['Close'] / df['Close'].iloc[0], label='Buy‑and‑Hold (norm)', alpha=0.4)
plt.title('Equity Curve – EMA20 + RSI14 Signal')
plt.legend()
plt.show()
Enter fullscreen mode Exit fullscreen mode

Whoa—look at that curve! The equity line climbs smoothly, with far fewer drawdowns. The EMA filters out the micro‑wiggles, and the RSI keeps us from chasing exhausted moves.

Common Traps (The “Boss Levels”)

  1. Look‑ahead bias – Never use tomorrow’s data to compute today’s indicator. In the snippet above, notice we shift the signal by one period (shift(1)) before applying it to returns. If you forget that shift, you’ll be peeking into the future and inflate performance dramatically.
  2. Wrong window length – A 5‑day EMA will hug price too tightly, giving you tons of whipsaws; a 200‑day EMA may be so laggy you miss entire trends. Experiment, but keep the window sensible for your timeframe (e.g., 20‑day for daily swings, 50‑day for intermediate).
  3. RSI on non‑stationary assets – RSI assumes the asset oscillates around a mean. For strongly trending crypto in a bull run, RSI can stay >70 for weeks, making the “overbought” signal useless. In those cases, adjust thresholds (e.g., 80/20) or complement RSI with trend‑following filters like the EMA we already added.

Why This New Power Matters

Armed with EMA and RSI, you’ve gone from reacting to every tick to reading the market’s pulse. You can now:

  • Build a simple trend‑following bot that only enters when the price respects the larger‑scale direction (EMA) and the momentum isn’t exhausted (RSI).
  • Reduce false entries, which translates to lower transaction costs and a calmer night’s sleep.
  • Layer additional indicators (MACD, Bollinger Bands) on top of this foundation, confident that your core signal isn’t just noise.

It’s like upgrading from a wooden sword to a lightsaber—you still need skill, but the tool lets you cut through the fog with precision.

Your Next Quest

Try this: take the same script, swap the EMA for a 50‑day SMA, and play with RSI thresholds (maybe 60/40) on a different asset—say, Ethereum or a tech stock. Plot the equity curve, note the changes, and ask yourself: Did smoothing the trend help or hurt? Did the RSI tweak catch more reversals?

Share your results in the comments—let’s compare notes like a party of adventurers swapping loot after a boss fight. Happy coding, and may your signals be ever in your favor! 🚀

Top comments (0)