Lesson 49 · Finance and Stock Market Analytics
Moving Averages and Smoothing Techniques in Stock Market Analysis
Learn to smooth noisy price data with moving averages Discover why smoothing is crucial for financial decision making Understand how to compute and use…
- CourseFinance and Stock Market Analytics
- Lesson49 of 16
- Video28 min
- FormatJupyter notebook · 16 code cells
What you'll learn
- Understanding Stock Data and Smoothing
- Example 1: Simple Moving Average (Beginner)
- Example 2: Visualizing Price and Moving Average (Beginner)
- Example 3: Customizing Moving Average Windows (Beginner)
- Example 4: Moving Average for Multiple Stocks (Intermediate)
- Example 5: Detecting Moving Average Crossovers (Intermediate)
- Example 6: Highlighting Buy and Sell Signals with Moving Averages (Intermediate)
- Example 7: Exponential Moving Average (EMA) (Advanced)
Data
No separate download needed — the notebook creates or downloads everything it uses.
📓 Full notebook
Download .ipynbMoving Averages and Smoothing Techniques in Stock Market Analysis#
- Learn to smooth noisy price data with moving averages
- Discover why smoothing is crucial for financial decision making
- Understand how to compute and use multiple moving averages
- Build and debug real Python code to generate actionable signals
- See a full pipeline from raw prices to trading insights
- Practice with real stock data for major tech tickers
import warnings
warnings.filterwarnings('ignore')
import pandas as pd
import numpy as np
import yfinance as yf
Understanding Stock Data and Smoothing#
- In finance, daily prices for stocks are often noisy and volatile
- Moving averages help reveal mid-term or long-term trends
- Real stock datasets have missing values, gaps, or unexpected outliers
- Beginners often confuse short-term and long-term smoothing
- Our data will include multiple tickers, date columns, and daily prices
# Load OHLCV data for five top tech stocks for 1 year (multi-ticker, long format)
tickers = ['AAPL','MSFT','GOOGL','AMZN','TSLA']
ohlcv = yf.download(tickers, period='1y', auto_adjust=True, progress=False)
ohlcv = ohlcv.stack(future_stack=True).rename_axis(['Date','Ticker']).reset_index()
ohlcv.columns.name = None
print(ohlcv.shape)
print(ohlcv.head(3))
Example 1: Simple Moving Average (Beginner)#
- Let us calculate the 10-day moving average for Apple (AAPL) closing prices
- Moving averages are often used to reduce noise and spot trends
- The rolling() method in pandas makes this process simple
# Filter for just AAPL
aapl = ohlcv[ohlcv['Ticker'] == 'AAPL'].copy()
aapl['MA10'] = aapl['Close'].rolling(window=10).mean().round(2)
print(aapl[['Date','Close','MA10']].head(12))
Example 2: Visualizing Price and Moving Average (Beginner)#
- Visual charts make it easier to compare raw and smoothed prices
- Let us create a quick plot for Apple closing prices and its 10-day moving average
- Practice reading the smoothed trend line
import matplotlib.pyplot as plt
plt.figure(figsize=(10,4))
plt.plot(aapl['Date'], aapl['Close'], label='AAPL Close', alpha=0.6)
plt.plot(aapl['Date'], aapl['MA10'], label='10-Day MA', color='orange')
plt.legend(loc='upper left')
plt.title('Apple Closing Price vs. 10-Day Moving Average')
plt.xlabel('Date')
plt.ylabel('Price (USD)')
plt.tight_layout()
plt.show()
Example 3: Customizing Moving Average Windows (Beginner)#
- Any window size can be used for moving averages
- Short windows respond quickly but can be noisy
- Long windows smooth more slowly but lag behind trends
aapl['MA3'] = aapl['Close'].rolling(window=3).mean().round(2)
aapl['MA30'] = aapl['Close'].rolling(window=30).mean().round(2)
print(aapl[['Date','Close','MA3','MA10','MA30']].head(15))
Example 4: Moving Average for Multiple Stocks (Intermediate)#
- Let us calculate a 20-day moving average for every stock in our dataset
- This approach works for all tickers, not just one
- Be careful to group by ticker before calculating rolling means
ohlcv['MA20'] = ohlcv.groupby('Ticker')['Close'].transform(lambda x: x.rolling(window=20).mean())
ohlcv['MA20'] = ohlcv['MA20'].round(2)
print(ohlcv.loc[ohlcv['Date'] == ohlcv['Date'].min()', ['Date', 'Ticker', 'Close', 'MA20']].head())
Example 5: Detecting Moving Average Crossovers (Intermediate)#
- A common trading strategy is to look for short-term versus long-term moving average crossovers
- When a fast MA crosses above a slow MA, this can be a bullish signal
- When a fast MA crosses below a slow MA, this can be a bearish signal
ohlcv['MA50'] = ohlcv.groupby('Ticker')['Close'].transform(lambda x: x.rolling(window=50).mean()).round(2)
ohlcv['crossover'] = np.where(ohlcv['MA20'] > ohlcv['MA50'], 1, -1)
print(ohlcv[ohlcv['Ticker']=='AAPL'][['Date','Close','MA20','MA50','crossover']].tail(10))
Example 6: Highlighting Buy and Sell Signals with Moving Averages (Intermediate)#
- Combine price, MA20, MA50, and crossovers to visually highlight potential trading opportunities
- Visualization makes it easier to see why signals were generated
signal_aapl = ohlcv[ohlcv['Ticker']=='AAPL'].copy()
plt.figure(figsize=(12,6))
plt.plot(signal_aapl['Date'], signal_aapl['Close'], label='AAPL Close', alpha=0.6)
plt.plot(signal_aapl['Date'], signal_aapl['MA20'], label='20D MA', color='orange')
plt.plot(signal_aapl['Date'], signal_aapl['MA50'], label='50D MA', color='green')
buy = signal_aapl[signal_aapl['crossover'] == 1]
sell = signal_aapl[signal_aapl['crossover'] == -1]
plt.scatter(buy['Date'], buy['Close'], marker='^', color='blue', label='Potential Buy', alpha=0.9)
plt.scatter(sell['Date'], sell['Close'], marker='v', color='red', label='Potential Sell', alpha=0.6)
plt.legend(loc='upper left')
plt.title('AAPL: 20/50-Day Moving Average Crossovers')
plt.xlabel('Date')
plt.ylabel('Price (USD)')
plt.tight_layout()
plt.show()
Example 7: Exponential Moving Average (EMA) (Advanced)#
- EMA weights recent prices more heavily, offering faster trend response
- EMAs are standard in many trading toolkits
aapl['EMA20'] = aapl['Close'].ewm(span=20, adjust=False).mean().round(2)
print(aapl[['Date','Close','MA20','EMA20']].tail(12))
Example 8: Bollinger Bands to Visualize Price Extremes (Advanced)#
- Bollinger Bands use a moving average and price volatility to highlight extreme values
- These are widely used for risk management and identifying possible overbought/oversold points
aapl['STD20'] = aapl['Close'].rolling(window=20).std()
aapl['BB_upper'] = aapl['MA20'] + 2*aapl['STD20']
aapl['BB_lower'] = aapl['MA20'] - 2*aapl['STD20']
plt.figure(figsize=(10,5))
plt.plot(aapl['Date'], aapl['Close'], label='AAPL Close', alpha=0.6)
plt.plot(aapl['Date'], aapl['MA20'], label='20D MA', color='black')
plt.fill_between(aapl['Date'], aapl['BB_upper'], aapl['BB_lower'], color='grey', alpha=0.2, label='Bollinger Band')
plt.legend(loc='upper left')
plt.title('AAPL with 20-Day Bollinger Bands')
plt.xlabel('Date')
plt.ylabel('Price (USD)')
plt.tight_layout()
plt.show()
Example 9: Handling Missing Data in Rolling Windows (Advanced)#
- Real datasets sometimes have gaps in price or volume for certain dates
- Moving averages ignore missing values, but can produce unexpected NaN output
# Simulate missing price for several days
aapl_missing = aapl.copy()
aapl_missing.loc[5:7, 'Close'] = np.nan
aapl_missing['MA10'] = aapl_missing['Close'].rolling(window=10).mean()
print(aapl_missing[['Date', 'Close', 'MA10']].head(15))
Debugging: Avoiding Mistakes with Multi-Ticker Data#
- Never compute moving averages across all stocks at once
- Group by ticker to keep calculations isolated
- Watch for column name confusion with multi-ticker yfinance data
# BAD EXAMPLE: This would mix tickers if you forgot to groupby
bad_ma = ohlcv['Close'].rolling(window=10).mean()
print(bad_ma.dropna().head(5)) # Output is incorrect!
Best Practices for Financial Smoothing#
- Always group by asset or ticker before applying rolling or smoothing
- Choose window sizes to match your time horizon
- Plot before trusting any signal
- Watch out for boundary NaNs on small datasets
- Use set seed to 42 for reproducible results when random numbers are required
# Example for reproducibility: setting random seed and showing a random selection
np.random.seed(42)
rand_dates = np.random.choice(aapl['Date'], 3, replace=False)
print(rand_dates)
End-to-End Problem: Generating a Moving Average Buy/Sell Signal Table#
- Start with real stock prices
- Compute two moving averages
- Create buy/sell signals
- Output the complete signals table to CSV
signal_table = ohlcv[ohlcv['Ticker']=='MSFT'][['Date','Close']].copy()
signal_table['SMA20'] = signal_table['Close'].rolling(window=20).mean().round(2)
signal_table['SMA50'] = signal_table['Close'].rolling(window=50).mean().round(2)
signal_table['Signal'] = np.where(signal_table['SMA20'] > signal_table['SMA50'], 'Buy', 'Sell')
signal_table.dropna().to_csv('msft_signals.csv', index=False)
print(signal_table.dropna().head(7))
# Clean up resources (optional practice step for good habits)
del ohlcv, aapl, signal_table
Found this useful?
All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.



