| import ta | |
| import pandas as pd | |
| def analyze_technical(market_data): | |
| results = {} | |
| for ticker, df in market_data.items(): | |
| try: | |
| df = df.copy() | |
| # Ensure 1D Series (fix for yfinance issue) | |
| close = pd.Series(df["Close"]).squeeze() | |
| volume = pd.Series(df["Volume"]).squeeze() | |
| score = 0 | |
| rsi = ta.momentum.RSIIndicator(close).rsi() | |
| macd = ta.trend.MACD(close).macd() | |
| ma20 = close.rolling(20).mean() | |
| ma50 = close.rolling(50).mean() | |
| bb = ta.volatility.BollingerBands(close) | |
| bb_high = bb.bollinger_hband() | |
| bb_low = bb.bollinger_lband() | |
| last = len(close) - 1 | |
| # RSI oversold | |
| if rsi.iloc[last] < 35: | |
| score += 1 | |
| # MACD bullish | |
| if macd.iloc[last] > 0: | |
| score += 1 | |
| # MA crossover | |
| if ma20.iloc[last] > ma50.iloc[last]: | |
| score += 1 | |
| # Bollinger bounce | |
| if close.iloc[last] < bb_low.iloc[last]: | |
| score += 1 | |
| # Volume spike | |
| if volume.iloc[last] > volume.rolling(20).mean().iloc[last]: | |
| score += 1 | |
| results[ticker] = score | |
| except: | |
| results[ticker] = 0 | |
| return results |