| import yfinance as yf | |
| def calc_rsi(close, period=14): | |
| delta = close.diff() | |
| gain = delta.clip(lower=0).rolling(period).mean() | |
| loss = (-delta.clip(upper=0)).rolling(period).mean() | |
| return float((100 - 100 / (1 + gain / loss)).iloc[-1]) | |
| def calc_macd_histogram(close): | |
| ema12 = close.ewm(span=12).mean() | |
| ema26 = close.ewm(span=26).mean() | |
| macd = ema12 - ema26 | |
| signal = macd.ewm(span=9).mean() | |
| return float((macd - signal).iloc[-1]) | |
| def calc_bb_position(current, close): | |
| bb_mid = close.rolling(20).mean().iloc[-1] | |
| bb_std = close.rolling(20).std().iloc[-1] | |
| bb_upper = float(bb_mid + 2 * bb_std) | |
| bb_lower = float(bb_mid - 2 * bb_std) | |
| pos = (current - bb_lower) / (bb_upper - bb_lower) * 100 \ | |
| if (bb_upper - bb_lower) > 0 else 50 | |
| if pos > 80: | |
| label = f"์๋จ ๊ทผ์ ({pos:.0f}%)" | |
| elif pos < 20: | |
| label = f"ํ๋จ ๊ทผ์ ({pos:.0f}%)" | |
| else: | |
| label = f"์ค๊ฐ ๊ตฌ๊ฐ ({pos:.0f}%)" | |
| return bb_upper, bb_lower, label | |
| def fetch_technicals(ticker, period="6mo"): | |
| hist = yf.Ticker(ticker).history(period=period) | |
| if len(hist) < 30: | |
| return {} | |
| close = hist["Close"] | |
| volume = hist["Volume"] | |
| current = float(close.iloc[-1]) | |
| ma20 = close.rolling(20).mean().iloc[-1] | |
| ma50 = close.rolling(50).mean().iloc[-1] if len(close) >= 50 else None | |
| ma200 = close.rolling(200).mean().iloc[-1] if len(close) >= 200 else None | |
| rsi = calc_rsi(close) | |
| macd_hist = calc_macd_histogram(close) | |
| bb_upper, bb_lower, bb_pos = calc_bb_position(current, close) | |
| def rsi_signal(r): | |
| if r < 30: return "๊ณผ๋งค๋ (๋ฐ๋ฑ ๊ฐ๋ฅ)" | |
| if r > 70: return "๊ณผ๋งค์ (์กฐ์ ๊ฐ๋ฅ)" | |
| return "์ค๋ฆฝ" | |
| return { | |
| "ma20": round(float(ma20), 2), | |
| "ma50": round(float(ma50), 2) if ma50 is not None else None, | |
| "ma200": round(float(ma200), 2) if ma200 is not None else None, | |
| "price_vs_ma20": "์" if current > float(ma20) else "์๋", | |
| "rsi_14": round(rsi, 1), | |
| "rsi_signal": rsi_signal(rsi), | |
| "macd_histogram": round(macd_hist, 4), | |
| "macd_signal": "์์น ๋ชจ๋ฉํ " if macd_hist > 0 else "ํ๋ฝ ๋ชจ๋ฉํ ", | |
| "bb_upper": round(bb_upper, 2), | |
| "bb_lower": round(bb_lower, 2), | |
| "bb_position": bb_pos, | |
| "volume_ratio": round(float(volume.iloc[-1]) / float(volume.tail(20).mean()), 2), | |
| } | |