Generating Trading Signals Using SMA, MACD, and Bollinger Bands
Written on
Chapter 1: Understanding the Basics of Trading Signals
In trading, there are primarily two philosophies that guide decisions on when to buy and sell stocks: Technical Analysis and Fundamental Analysis.
Technical Analysis focuses on examining price movements and patterns of a stock or ticker. In contrast, Fundamental Analysis assesses metrics such as cash flow, revenue, valuation, and industry trends.
While Technical Analysis relies heavily on numerical data—particularly price and volume—its effectiveness is often seen in the short term. Conversely, for long-term growth, the strength of the underlying business remains crucial.
In essence, Technical Analysis aims to predict the future prices of financial assets by analyzing historical market data, rooted in principles like the belief that history tends to repeat itself, all information is reflected in price, and trends can be identified in time-series data.
Technical Analysis is the foundation for:
Algorithmic, Automated, or Rule-Based Trading. Indicators such as the Relative Strength Index (RSI), Moving Averages, Oscillators, and Candlestick Patterns are employed to identify overbought or oversold conditions, trend strength, or potential reversals. This article will explore how to generate buy and sell signals using some of these indicators.
Module Utilized: Pandas TA
Pandas TA is a user-friendly library built on top of Pandas that offers over 130 indicators and utility functions, along with more than 60 candlestick patterns.
To install the library, simply open your terminal, activate your conda environment, and run the following command:
pip install pandas-ta
1.1 Importing Required Libraries
To begin, we need several packages including Pandas and NumPy. If these are not installed, you can do so using pip:
import numpy as np
import pandas as pd
import yfinance as yf
import pandas_datareader.data as web
import pandas_ta as ta
import matplotlib.pyplot as plt
from datetime import date
plt.style.use('fivethirtyeight')
yf.pdr_override()
1.2 Data Extraction
For our analysis, we will use TATAMOTORS, a highly liquid stock that is well-suited for our strategies. The following code extracts the necessary data:
stocksymbols = ['TATAMOTORS.NS']
startdate = date(2017, 8, 4)
end_date = date.today()
print(end_date)
def getMyPortfolio(stocks=stocksymbols, start=startdate, end=end_date):
data = web.get_data_yahoo(stocks, data_source='yahoo', start=start, end=end)
return data
data = getMyPortfolio(stocksymbols)
1.3 Strategy Implementation
#### Simple Moving Averages (SMA)
We will begin with a straightforward indicator, the Simple Moving Average. This involves averaging the closing prices over a specified period to smooth out trends and illustrate the general direction:
data['SMA 30'] = ta.sma(data['Close'], 30)
data['SMA 100'] = ta.sma(data['Close'], 100)
#### Buy and Sell Signal Function
Next, we implement a function to generate buy and sell signals based on the SMA strategy:
def buy_sell(data):
signalBuy = []
signalSell = []
position = False
for i in range(len(data)):
if data['SMA 30'][i] > data['SMA 100'][i]:
if not position:
signalBuy.append(data['Adj Close'][i])
signalSell.append(np.nan)
position = True
else:
signalBuy.append(np.nan)
signalSell.append(np.nan)
elif data['SMA 30'][i] < data['SMA 100'][i]:
if position:
signalBuy.append(np.nan)
signalSell.append(data['Adj Close'][i])
position = False
else:
signalBuy.append(np.nan)
signalSell.append(np.nan)
else:
signalBuy.append(np.nan)
signalSell.append(np.nan)
return pd.Series([signalBuy, signalSell])
data['Buy_Signal_price'], data['Sell_Signal_price'] = buy_sell(data)
1.4 Visualization
To visualize the buy and sell signals, we can create a plot:
fig, ax = plt.subplots(figsize=(14, 8))
ax.plot(data['Adj Close'], label=stocksymbols[0], linewidth=0.5, color='blue', alpha=0.9)
ax.plot(data['SMA 30'], label='SMA30', alpha=0.85)
ax.plot(data['SMA 100'], label='SMA100', alpha=0.85)
ax.scatter(data.index, data['Buy_Signal_price'], label='Buy', marker='^', color='green', alpha=1)
ax.scatter(data.index, data['Sell_Signal_price'], label='Sell', marker='v', color='red', alpha=1)
ax.set_title(f"{stocksymbols[0]} Price History with Buy and Sell Signals", fontsize=10, backgroundcolor='blue', color='white')
ax.set_xlabel(f'{startdate} - {end_date}', fontsize=18)
ax.set_ylabel('Close Price INR (₨)', fontsize=18)
legend = ax.legend()
ax.grid()
plt.tight_layout()
plt.show()
Output:
This code visualizes our buy and sell levels, indicating that moving averages are effective for identifying long-term trends, aiding in decisions to buy, sell, or hold.
1.5 Moving Average Convergence Divergence (MACD)
The MACD indicator utilizes two exponential moving averages (EMA)—a short-term and a long-term. It provides three columns: the MACD line, the Signal line (EMA of the MACD), and the MACD histogram.
macd = ta.macd(data['Close'])
data = pd.concat([data, macd], axis=1).reindex(data.index)
When the MACD line crosses above the Signal line, a long position is recommended. Conversely, a cross below indicates a potential short position.
1.6 MACD Strategy Implementation
The following function applies our MACD strategy with a risk factor:
def MACD_Strategy(df, risk):
MACD_Buy = []
MACD_Sell = []
position = False
for i in range(len(df)):
if df['MACD_12_26_9'][i] > df['MACDs_12_26_9'][i]:
MACD_Sell.append(np.nan)
if not position:
MACD_Buy.append(df['Adj Close'][i])
position = True
else:
MACD_Buy.append(np.nan)elif df['MACD_12_26_9'][i] < df['MACDs_12_26_9'][i]:
MACD_Buy.append(np.nan)
if position:
MACD_Sell.append(df['Adj Close'][i])
position = False
else:
MACD_Sell.append(np.nan)elif position and df['Adj Close'][i] < MACD_Buy[-1] * (1 - risk):
MACD_Sell.append(df["Adj Close"][i])
MACD_Buy.append(np.nan)
position = False
elif position and df['Adj Close'][i] < df['Adj Close'][i - 1] * (1 - risk):
MACD_Sell.append(df["Adj Close"][i])
MACD_Buy.append(np.nan)
position = False
else:
MACD_Buy.append(np.nan)
MACD_Sell.append(np.nan)
df['MACD_Buy_Signal_price'] = MACD_Buy
df['MACD_Sell_Signal_price'] = MACD_Sell
data = MACD_Strategy(data, 0.025)
1.7 MACD Visualization
To visualize the MACD signals:
plt.rcParams.update({'font.size': 10})
fig, ax1 = plt.subplots(figsize=(14, 8))
fig.suptitle(stocksymbols[0], fontsize=10, backgroundcolor='blue', color='white')
ax1.set_ylabel('Price in ₨')
ax1.plot('Adj Close', data=data, label='Close Price', linewidth=0.5, color='blue')
ax1.scatter(data.index, data['MACD_Buy_Signal_price'], color='green', marker='^', alpha=1)
ax1.scatter(data.index, data['MACD_Sell_Signal_price'], color='red', marker='v', alpha=1)
ax1.legend()
ax1.grid()
ax1.set_xlabel('Date', fontsize=8)
ax2 = plt.subplot2grid((14, 8), (10, 0), rowspan=6, colspan=14)
ax2.set_ylabel('MACD', fontsize=8)
ax2.plot('MACD_12_26_9', data=data, label='MACD', linewidth=0.5, color='blue')
ax2.plot('MACDs_12_26_9', data=data, label='Signal', linewidth=0.5, color='red')
ax2.bar(data.index, 'MACDh_12_26_9', data=data, label='Volume', color=data.positive.map({True: 'g', False: 'r'}), width=1, alpha=0.8)
ax2.axhline(0, color='black', linewidth=0.5, alpha=0.5)
ax2.grid()
plt.show()
1.8 Bollinger Bands (BB)
Bollinger Bands are commonly used in trading due to their power and simplicity. They consist of three lines: the Upper Band, the Middle Band, and the Lower Band. The upper and lower bands are typically set two standard deviations away from the mean closing price, capturing over 80% of price action.
#### BB Strategy Function
def bb_strategy(data):
bbBuy = []
bbSell = []
position = False
bb = ta.bbands(data['Adj Close'], length=20, std=2)
data = pd.concat([data, bb], axis=1).reindex(data.index)
for i in range(len(data)):
if data['Adj Close'][i] < data['BBL_20_2.0'][i]:
if not position:
bbBuy.append(data['Adj Close'][i])
bbSell.append(np.nan)
position = True
else:
bbBuy.append(np.nan)
bbSell.append(np.nan)
elif data['Adj Close'][i] > data['BBU_20_2.0'][i]:
if position:
bbBuy.append(np.nan)
bbSell.append(data['Adj Close'][i])
position = False
else:
bbBuy.append(np.nan)
bbSell.append(np.nan)
else:
bbBuy.append(np.nan)
bbSell.append(np.nan)
data['bb_Buy_Signal_price'] = bbBuy
data['bb_Sell_Signal_price'] = bbSell
return data
data = bb_strategy(data)
1.9 BB Visualization
To visualize the Bollinger Bands:
fig, ax1 = plt.subplots(figsize=(14, 8))
fig.suptitle(stocksymbols[0], fontsize=10, backgroundcolor='blue', color='white')
ax1.set_ylabel('Price in ₨')
ax1.plot(data['Adj Close'], label='Close Price', linewidth=0.5, color='blue')
ax1.scatter(data.index, data['bb_Buy_Signal_price'], color='green', marker='^', alpha=1)
ax1.scatter(data.index, data['bb_Sell_Signal_price'], color='red', marker='v', alpha=1)
ax1.legend()
ax1.grid()
ax1.set_xlabel('Date', fontsize=8)
ax2 = plt.subplot2grid((14, 8), (10, 0), rowspan=6, colspan=14)
ax2.plot(data['BBM_20_2.0'], label='Middle', color='blue', alpha=0.35)
ax2.plot(data['BBU_20_2.0'], label='Upper', color='green', alpha=0.35)
ax2.plot(data['BBL_20_2.0'], label='Lower', color='red', alpha=0.35)
ax2.fill_between(data.index, data['BBL_20_2.0'], data['BBU_20_2.0'], alpha=0.1)
ax2.legend(loc='upper left')
ax2.grid()
plt.show()
Output:
Our strategy assumes that any closing price that breaches the Bollinger Bands signals a high probability of price reversion. However, caution is advised, as external events may influence stock behavior.
Conclusion
In conclusion, the realm of Technical Analysis is extensive, with numerous indicators available. Understanding the fundamental principles behind these indicators is vital for crafting a successful trading strategy, whether by combining them or developing entirely new indicators. As we delve deeper into Algorithmic Trading using these technical indicators, the complexity will inevitably increase. I hope you found this article informative!
Learn how to build a Bollinger Bands and RSI trading strategy using Python.
Discover how to create buy and sell signals with Bollinger Bands on the MACD indicator in TradingView PineScript V5.