import pytz import requests import pandas as pd import yfinance as yf import numpy as np from bs4 import BeautifulSoup import gradio as gr from concurrent.futures import ThreadPoolExecutor from datetime import datetime # 🔗 URL Yahoo Finance Most Active Stocks YAHOO_FINANCE_URL = "https://finance.yahoo.com/markets/stocks/most-active/?start=25&count=100" # 📌 Lấy danh sách 100 cổ phiếu hoạt động mạnh nhất từ Yahoo def fetch_most_active_stocks(): headers = {"User-Agent": "Mozilla/5.0"} response = requests.get(YAHOO_FINANCE_URL, headers=headers) if response.status_code != 200: print(f"❌ Lỗi HTTP {response.status_code}") return [] soup = BeautifulSoup(response.text, 'html.parser') table = soup.find('table') if not table: print("❌ Không tìm thấy bảng dữ liệu!") return [] rows = table.find_all('tr')[1:] stocks = [] for row in rows: cols = row.find_all('td') if cols: ticker = cols[0].text.strip() stocks.append(ticker) return stocks[:100] # 📈 Giới hạn 100 cổ phiếu # 📌 Tính toán Williams %R (14 kỳ), RSI (14), MACD, Bollinger Bands def calculate_technical_indicators(ticker): try: stock = yf.Ticker(ticker) hist = stock.history(period="3mo") # Lấy dữ liệu 3 tháng để tính toán if hist.empty: return "N/A", "N/A", "N/A", "N/A" # Williams %R (14 kỳ) high14 = hist['High'].rolling(window=14).max() low14 = hist['Low'].rolling(window=14).min() close = hist['Close'] williams_r = -100 * ((high14 - close) / (high14 - low14)) williams_r = round(williams_r.iloc[-1], 2) if not np.isnan(williams_r.iloc[-1]) else "N/A" # RSI (14 kỳ) delta = close.diff(1) gain = (delta.where(delta > 0, 0)).rolling(window=14).mean() loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean() rs = gain / loss rsi = 100 - (100 / (1 + rs)) rsi = round(rsi.iloc[-1], 2) if not np.isnan(rsi.iloc[-1]) else "N/A" # MACD (12,26,9) ema12 = close.ewm(span=12, adjust=False).mean() ema26 = close.ewm(span=26, adjust=False).mean() macd = ema12 - ema26 signal = macd.ewm(span=9, adjust=False).mean() macd_signal = round(macd.iloc[-1] - signal.iloc[-1], 2) if not np.isnan(macd.iloc[-1]) else "N/A" # Bollinger Bands (20,2) sma20 = close.rolling(window=20).mean() stddev = close.rolling(window=20).std() upper_band = sma20 + (stddev * 2) lower_band = sma20 - (stddev * 2) bollinger_signal = "Buy" if close.iloc[-1] <= lower_band.iloc[-1] else "Sell" if close.iloc[-1] >= upper_band.iloc[-1] else "Neutral" return williams_r, rsi, macd_signal, bollinger_signal except: return "N/A", "N/A", "N/A", "N/A" # 📌 Phân tích quyền chọn và xu hướng def analyze_stock_options(ticker): try: stock = yf.Ticker(ticker) current_price = stock.history(period="1d")['Close'].iloc[-1] williams_r, rsi, macd_signal, bollinger_signal = calculate_technical_indicators(ticker) try: options = stock.option_chain() calls = options.calls puts = options.puts except: return None total_call_volume = calls['volume'].sum() total_put_volume = puts['volume'].sum() pcr = total_put_volume / total_call_volume if total_call_volume > 0 else np.nan try: iv_call = calls['impliedVolatility'].mean() iv_put = puts['impliedVolatility'].mean() iv_skew = round(iv_call - iv_put, 2) if isinstance(iv_call, float) and isinstance(iv_put, float) else "N/A" except: iv_skew = "N/A" trend = "📈 TĂNG" if pcr < 0.7 else "📉 GIẢM" if pcr > 1.0 else "➖ NEUTRAL" return { 'Ticker': ticker, 'Current Price': round(current_price, 2), 'Williams %R': williams_r, 'RSI': rsi, 'MACD': macd_signal, 'Signal': bollinger_signal, 'PCR': round(pcr, 2) if not np.isnan(pcr) else "N/A", 'IV Skew': iv_skew, 'Trend': trend } except: return None # 📌 Chạy ứng dụng trên Gradio def display_results(stock_symbol): stock_list = [s.strip().upper() for s in stock_symbol.split(",")] if stock_symbol else fetch_most_active_stocks() valid_stocks = [s for s in stock_list if yf.Ticker(s).info.get("symbol")] if not valid_stocks: return "❌ Không có dữ liệu hợp lệ!" data = list(map(analyze_stock_options, valid_stocks)) df = pd.DataFrame([d for d in data if d is not None]) # Loại bỏ None nếu có if df.empty: return "❌ Không có dữ liệu!" df.insert(0, "STT", range(1, len(df) + 1)) # Định nghĩa thứ tự cột mong muốn table_columns = ["STT", "Trend", "Ticker", "Current Price", "Signal", "RSI", "MACD", "Williams %R", "PCR", "IV Skew"] # Kiểm tra nếu cột tồn tại trước khi sắp xếp if not df.empty: df = df[[col for col in table_columns if col in df.columns]] return df title = f"📊 Phân tích xu hướng cổ phiếu " iface = gr.Interface( fn=display_results, inputs=gr.Textbox(placeholder="Nhập mã cổ phiếu cách nhau bằng dấu phẩy (VD: AAPL, TSLA, NVDA)", label="📌 Nhập mã cổ phiếu (Tùy chọn)"), outputs="dataframe", title=title, description="""🔍 **Dự đoán xu hướng 📈 TĂNG / 📉 GIẢM** 🚀 Hướng dẫn sử dụng 1. **Nhập mã cổ phiếu** (VD: `AAPL, TSLA, NVDA`) hoặc **để trống** (hệ thống sẽ tự lấy mã cổ phiếu). 2. **Nhấn nút "Submit"** để phân tích xu hướng. (nếu **để trống** thời gian quét sẽ mất tầm 50 giây) 3. **Ứng dụng sẽ tự động phân tích** và dự đoán 📈 TĂNG hoặc 📉 GIẢM. """) iface.launch()