import math
import yfinance as yf
from [Link] import display, HTML
# Data Abstraction: List of valid stock tickers for input validation and display (student
developed)
dowstocks = ["AAPL", "MSFT", "JPM", "DIS", "BA", "GS", "IBM", "INTC", "CSCO", "VZ",
"WMT", "HD", "CVX", "MRK", "KO", "PG", "JNJ", "MMM", "AXP", "NKE",
"CAT", "TRV", "UNH", "V", "WBA", "XOM", "PFE", "RTX", "DOW", "AMGN",
"TSLA", "NVDA", "MSTR", "GOOG", "F", "AMD", "CLSK", "HOOD", "BTC-USD",
"MLGO", "PLTR", "LCID", "NIO", "SOFI", "AMZN", "AAL", "W", "CVNA", "RIOT",
"GAP", "ZETA", "SPY"]
# Helper Functions for Technical Indicators
def calculate_ema(prices, period): #steudent developed
"""Calculate Exponential Moving Average; returns None if data insufficient."""
if len(prices) < period:
return None
multiplier = 2 / (period + 1)
ema = prices[-period]
for price in prices[-period + 1:]:
ema = (price * multiplier) + (ema * (1 - multiplier))
return ema
def calculate_wma(prices, period): #student developed
"""Calculate Weighted Moving Average; returns None if data insufficient."""
if len(prices) < period:
return None
weights = list(range(1, period + 1))
weighted_sum = sum(p * w for p, w in zip(prices[-period:], weights))
return weighted_sum / sum(weights)
def calculate_rsi(prices, period=11): #(made by claude sonnet 3.7)
"""Calculate Relative Strength Index; returns None if data insufficient."""
if len(prices) < period + 1:
return None
gains = 0
losses = 0
for i in range(1, len(prices)):
diff = prices[i] - prices[i-1]
if diff > 0:
gains += diff
elif diff < 0:
losses -= diff
avg_gain = gains / period
avg_loss = losses / period
if avg_loss == 0:
return 100
rs = avg_gain / avg_loss
return 100 - (100 / (1 + rs))
def calculate_macd(prices, short_period=3, long_period=6, signal_period=2):#this was
made by grok 3
"""Calculate MACD (line, signal, histogram); returns None if data insufficient."""
if len(prices) < long_period:
return None, None, None
short_ema = calculate_ema(prices, short_period)
long_ema = calculate_ema(prices, long_period)
macd_line = short_ema - long_ema
signal_prices = prices[-signal_period:] if len(prices) >= signal_period else prices
signal_line = calculate_ema(signal_prices, signal_period) if signal_prices else None
histogram = macd_line - signal_line if signal_line else None
return macd_line, signal_line, histogram
def calculate_bollinger_bands(prices, period=10, std_dev=2): #This was made by grok
ai
"""Calculate Bollinger Bands (upper, middle, lower); returns None if data
insufficient."""
if len(prices) < period:
return None, None, None
sma = sum(prices[-period:]) / period
squared_diff_sum = 0
for price in prices[-period:]:
squared_diff_sum += (price - sma) ** 2
std = [Link](squared_diff_sum / period)
upper_band = sma + (std_dev * std)
lower_band = sma - (std_dev * std)
return upper_band, sma, lower_band
def calculate_stochastic(prices, k_period=5, d_period=3): #(This was made by grok ai)
"""Calculate Stochastic Oscillator (%K, %D); returns None if data insufficient."""
if len(prices) < k_period + d_period - 1:
return None, None
k_values = []
for i in range(len(prices) - k_period + 1):
window = prices[i:i + k_period]
current_close = window[-1]
lowest_low = min(window)
highest_high = max(window)
k_value = 50 if highest_high - lowest_low == 0 else 100 * ((current_close -
lowest_low) / (highest_high - lowest_low))
k_values.append(k_value)
k_value = k_values[-1]
d_value = sum(k_values[-d_period:]) / d_period if len(k_values) >= d_period else
None
return k_value, d_value
# Student-Developed Procedure for Stock Analysis (student developed)
def analyze_stock(ticker, prices):
"""Analyze stock data and return a Buy/Sell/Hold recommendation."""
# Sequencing: Calculate SMA first
period = 4
sma_3 = 0
for price in prices[-period:]: # Iteration: Loop over last 4 prices
sma_3 += price
sma_3 /= period
# Calculate technical indicators
ema_3 = calculate_ema(prices, 3)
wma_3 = calculate_wma(prices, 3)
rsi = calculate_rsi(prices, 11)
macd_line, signal_line, _ = calculate_macd(prices, 3, 6, 2)
upper_band, bb_middle, lower_band = calculate_bollinger_bands(prices, 10, 2)
stoch_k, stoch_d = calculate_stochastic(prices, 5, 3)
# Selection: Evaluate signals
current_price = prices[-1]
buy_signals = 0
sell_signals = 0
if current_price > sma_3:
buy_signals += 1
elif current_price < sma_3:
sell_signals += 1
if ema_3 and current_price > ema_3:
buy_signals += 1
elif ema_3 and current_price < ema_3:
sell_signals += 1
if wma_3 and current_price > wma_3:
buy_signals += 1
elif wma_3 and current_price < wma_3:
sell_signals += 1
if rsi is not None:
if rsi < 30:
buy_signals += 1
elif rsi > 70:
sell_signals += 1
if macd_line is not None and signal_line is not None:
if macd_line > signal_line:
buy_signals += 1
elif macd_line < signal_line:
sell_signals += 1
if upper_band is not None and lower_band is not None:
if current_price < lower_band:
buy_signals += 1
elif current_price > upper_band:
sell_signals += 1
if stoch_k is not None:
if stoch_k < 20:
buy_signals += 1
elif stoch_k > 80:
sell_signals += 1
if stoch_d is not None:
if stoch_d < 20:
buy_signals += 1
elif stoch_d > 80:
sell_signals += 1
# Format output
rsi_display = f"{rsi:.2f}" if rsi is not None else "N/A"
macd_display = f"{macd_line:.2f}" if macd_line is not None else "N/A"
bb_display = f"{bb_middle:.2f} [{lower_band:.2f}, {upper_band:.2f}]" if bb_middle is
not None else "N/A"
stoch_display = f"K:{stoch_k:.2f}, D:{stoch_d:.2f}" if stoch_k is not None and stoch_d
is not None else f"K:{stoch_k:.2f}" if stoch_k is not None else "N/A"
# Selection: Determine recommendation
if buy_signals >= 4:
return f"{ticker}: Buy (SMA: {sma_3:.2f}, EMA: {ema_3:.2f}, WMA: {wma_3:.2f},
RSI: {rsi_display}, MACD: {macd_display}, BB: {bb_display}, Stoch: {stoch_display})"
elif sell_signals >= 4:
return f"{ticker}: Sell (SMA: {sma_3:.2f}, EMA: {ema_3:.2f}, WMA: {wma_3:.2f},
RSI: {rsi_display}, MACD: {macd_display}, BB: {bb_display}, Stoch: {stoch_display})"
else:
return f"{ticker}: Hold (SMA: {sma_3:.2f}, EMA: {ema_3:.2f}, WMA: {wma_3:.2f},
RSI: {rsi_display}, MACD: {macd_display}, BB: {bb_display}, Stoch: {stoch_display})"
# Utility Function for Consistent Output
def print_html(text):
"""Display text in white on black background in Colab."""
display(HTML(f"<p style='color:white; background-color:black;'>{text}</p>"))
# Main Program (This was made by the student)
print_html("Warning! This is not meant to be used for financial advice; any decisions
made by this program do not make the author responsible for any losses or gains.")
print_html("Welcome to the Dow Jones Stock Analyzer")
print_html("These are the available stocks that you can choose from: " + ",
".join(dowstocks))
stock_choices = []
print("Enter stock tickers below (type 'EXODIA' to quit):")
while True:
stock_choice = input().upper()
stock_choices.append(stock_choice)
if stock_choice == "EXODIA":
print_html("Program terminated.")
break
if stock_choice not in dowstocks:
print_html("Invalid ticker. Please select from the list above.")
continue
try:
stock = [Link](stock_choice)
hist = [Link](start="2005-01-01", end="2025-03-01", interval="1mo")
if [Link]:
print_html(f"No data available for {stock_choice}. Please try another ticker.")
continue
prices = hist['Close'].tolist()
if len(prices) < 12:
print_html(f"Insufficient data for {stock_choice}. At least 12 months required.")
continue
result = analyze_stock(stock_choice, prices)
print_html(result)
except Exception as e:
print_html(f"Error fetching data for {stock_choice}: {e}")
# Acknowledgment
# Assisted by Grok (xAI) to fix ValueError in f-string formatting, debug TypeError in RSI,
# add iteration to analyze_stock, integrate yfinance for accurate closing prices, and
update
# calculate_stochastic for full %K and %D calculation per user request.