Spaces:
Sleeping
Sleeping
Download app.py from AISparking/StockAnalysis: direct link, hf CLI and curl.
- Browser
- Download file 21.2 kB
-
https://huggingface.co/spaces/AISparking/StockAnalysis/resolve/main/app.py
- Command line
-
hf download hf://spaces/AISparking/StockAnalysis/app.py
-
curl -L -o app.py https://huggingface.co/spaces/AISparking/StockAnalysis/resolve/main/app.py
21.2 kB
| # FB Page: https://www.facebook.com/AIsparking | |
| import streamlit as st | |
| import yfinance as yf | |
| import pandas as pd | |
| import numpy as np | |
| from ta.trend import MACD | |
| from ta.momentum import RSIIndicator, StochasticOscillator | |
| from ta.volatility import AverageTrueRange, BollingerBands | |
| from ta.volume import VolumeWeightedAveragePrice | |
| import plotly.graph_objects as go | |
| from datetime import datetime, timedelta | |
| import plotly.subplots as sp | |
| from ta.trend import IchimokuIndicator | |
| # Set page config | |
| st.set_page_config(layout="wide", page_title="Stock Technical Analysis") | |
| # Functions from previous implementations remain the same | |
| # Add error handling wrapper | |
| def safe_execute(func): | |
| def wrapper(*args, **kwargs): | |
| try: | |
| return func(*args, **kwargs) | |
| except Exception as e: | |
| st.error(f"Error: {str(e)}") | |
| return None | |
| return wrapper | |
| def get_stock_data(symbol, market, nYear): | |
| if market == 'HK': | |
| symbol = f'{symbol}.HK' | |
| end_date = datetime.now() | |
| start_date = end_date - timedelta(days=nYear*365) | |
| df = yf.download(symbol, start=start_date, end=end_date) | |
| if df.empty: | |
| raise ValueError("No data found for this symbol") | |
| return df | |
| # Combine all technical indicators | |
| def generate_recommendation(df): | |
| last_row = df.iloc[-1] | |
| signals = [] | |
| # RSI signals | |
| if last_row['RSI'] < 30: | |
| signals.append(('BUY', 'RSI oversold')) | |
| elif last_row['RSI'] > 70: | |
| signals.append(('SELL', 'RSI overbought')) | |
| # Stochastic signals | |
| if last_row['%K'] < 20 and last_row['%D'] < 20: | |
| signals.append(('BUY', 'Stochastic oversold')) | |
| elif last_row['%K'] > 80 and last_row['%D'] > 80: | |
| signals.append(('SELL', 'Stochastic overbought')) | |
| # MACD signals | |
| if last_row['MACD'] > last_row['MACD_Signal']: | |
| signals.append(('BUY', 'MACD crossover')) | |
| elif last_row['MACD'] < last_row['MACD_Signal']: | |
| signals.append(('SELL', 'MACD crossunder')) | |
| return signals | |
| def calculate_indicators(df): | |
| # Calculate RSI | |
| rsi = RSIIndicator(df['Close']) | |
| df['RSI'] = rsi.rsi() | |
| # Calculate Stochastic | |
| stoch = StochasticOscillator(df['High'], df['Low'], df['Close']) | |
| df['%K'] = stoch.stoch() | |
| df['%D'] = stoch.stoch_signal() | |
| # Calculate MACD | |
| macd = MACD(df['Close']) | |
| df['MACD'] = macd.macd() | |
| df['MACD_Signal'] = macd.macd_signal() | |
| # Calculate ATR | |
| atr = AverageTrueRange(df['High'], df['Low'], df['Close']) | |
| df['ATR'] = atr.average_true_range() | |
| # Add multiple SMAs | |
| df['SMA20'] = df['Close'].rolling(window=20).mean() | |
| df['SMA50'] = df['Close'].rolling(window=50).mean() | |
| df['SMA100'] = df['Close'].rolling(window=100).mean() | |
| df['SMA200'] = df['Close'].rolling(window=200).mean() | |
| # Calculate ATR | |
| atr = AverageTrueRange(df['High'], df['Low'], df['Close']) | |
| df['ATR'] = atr.average_true_range() | |
| # Calculate VWAP | |
| vwap = VolumeWeightedAveragePrice(high=df['High'], | |
| low=df['Low'], | |
| close=df['Close'], | |
| volume=df['Volume']) | |
| df['VWAP'] = vwap.volume_weighted_average_price() | |
| return df | |
| def calculate_additional_indicators(df): | |
| # Add Bollinger Bands | |
| bb = BollingerBands(df['Close']) | |
| df['BB_upper'] = bb.bollinger_hband() | |
| df['BB_lower'] = bb.bollinger_lband() | |
| df['BB_middle'] = bb.bollinger_mavg() | |
| # Add Moving Averages | |
| df['MA50'] = df['Close'].rolling(window=50).mean() | |
| df['MA200'] = df['Close'].rolling(window=200).mean() | |
| # Add VWAP | |
| vwap = VolumeWeightedAveragePrice(high=df['High'], low=df['Low'], | |
| close=df['Close'], volume=df['Volume']) | |
| df['VWAP'] = vwap.volume_weighted_average_price() | |
| return df | |
| def calculate_ema(df): | |
| # Calculate EMAs | |
| df['EMA9'] = df['Close'].ewm(span=9, adjust=False).mean() | |
| df['EMA21'] = df['Close'].ewm(span=21, adjust=False).mean() | |
| return df | |
| def calculate_obv(df): | |
| # Calculate OBV | |
| obv = [] | |
| prev_obv = 0 | |
| for i in range(len(df)): | |
| if i == 0: | |
| obv.append(prev_obv) | |
| continue | |
| if df['Close'].iloc[i] > df['Close'].iloc[i-1]: | |
| current_obv = prev_obv + df['Volume'].iloc[i] | |
| elif df['Close'].iloc[i] < df['Close'].iloc[i-1]: | |
| current_obv = prev_obv - df['Volume'].iloc[i] | |
| else: | |
| current_obv = prev_obv | |
| obv.append(current_obv) | |
| prev_obv = current_obv | |
| df['OBV'] = obv | |
| return df | |
| def calculate_ichimoku(df): | |
| ichimoku = IchimokuIndicator(high=df['High'], low=df['Low']) | |
| df['ichimoku_a'] = ichimoku.ichimoku_a() | |
| df['ichimoku_b'] = ichimoku.ichimoku_b() | |
| df['ichimoku_base'] = ichimoku.ichimoku_base_line() | |
| df['ichimoku_conversion'] = ichimoku.ichimoku_conversion_line() | |
| return df | |
| def calculate_all_indicators(df): | |
| df = calculate_indicators(df) | |
| df = calculate_additional_indicators(df) | |
| df = calculate_ema(df) | |
| df = calculate_obv(df) | |
| df = calculate_ichimoku(df) | |
| return df | |
| def enhanced_recommendation(df): | |
| last_row = df.iloc[-1] | |
| prev_row = df.iloc[-2] | |
| signals = [] | |
| # Add EMA signals | |
| if last_row['EMA9'] > last_row['EMA21']: | |
| signals.append(('BUY', 'EMA9 crossed above EMA21')) | |
| elif last_row['EMA9'] < last_row['EMA21']: | |
| signals.append(('SELL', 'EMA9 crossed below EMA21')) | |
| # Add Ichimoku signals | |
| if (last_row['ichimoku_conversion'] > last_row['ichimoku_base'] and | |
| last_row['Close'] > last_row['ichimoku_a']): | |
| signals.append(('BUY', 'Ichimoku bullish signal')) | |
| elif (last_row['ichimoku_conversion'] < last_row['ichimoku_base'] and | |
| last_row['Close'] < last_row['ichimoku_b']): | |
| signals.append(('SELL', 'Ichimoku bearish signal')) | |
| # Add OBV signals | |
| if df['OBV'].iloc[-1] > df['OBV'].iloc[-2]: | |
| signals.append(('BUY', 'OBV increasing')) | |
| else: | |
| signals.append(('SELL', 'OBV decreasing')) | |
| # Add SMA signals | |
| if (last_row['SMA20'] > last_row['SMA50'] and | |
| prev_row['SMA20'] <= prev_row['SMA50']): | |
| signals.append(('BUY', 'SMA20 crossed above SMA50')) | |
| elif (last_row['SMA20'] < last_row['SMA50'] and | |
| prev_row['SMA20'] >= prev_row['SMA50']): | |
| signals.append(('SELL', 'SMA20 crossed below SMA50')) | |
| # Add VWAP signals | |
| if last_row['Close'] > last_row['VWAP']: | |
| signals.append(('BUY', 'Price above VWAP')) | |
| else: | |
| signals.append(('SELL', 'Price below VWAP')) | |
| # Add ATR-based volatility signals | |
| atr_threshold = df['ATR'].mean() * 1.5 | |
| if last_row['ATR'] > atr_threshold: | |
| signals.append(('HOLD', 'High volatility detected by ATR')) | |
| return signals | |
| # Main app layout | |
| st.title('Advanced Stock Technical Analysis') | |
| # Sidebar for inputs | |
| with st.sidebar: | |
| st.header('Input Parameters') | |
| market = st.selectbox('Select Market', ['HK','US']) | |
| symbol = st.text_input('Enter Stock Symbol (e.g. 0700 for HK):') | |
| # Add analysis timeframe option | |
| timeframe = st.selectbox('Select Timeframe', ['1y','2y','3y','5y','8y','10y']) | |
| nYear = int(timeframe.split("y")[0]) | |
| if st.sidebar.button('Analyze'): | |
| if symbol: | |
| with st.spinner('Fetching and analyzing data...'): | |
| df = get_stock_data(symbol, market,nYear) | |
| if df is not None: | |
| df = calculate_all_indicators(df) | |
| # Create tabs for different analyses | |
| tab1, tab2, tab3 = st.tabs(['Price Analysis', 'Technical Indicators', 'Technical Analysis']) | |
| with tab1: | |
| # Main price chart with volume | |
| fig = sp.make_subplots(rows=2, cols=1, shared_xaxes=True, | |
| vertical_spacing=0.03, row_heights=[0.7, 0.3]) | |
| fig.add_trace(go.Candlestick(x=df.index, open=df['Open'], | |
| high=df['High'], low=df['Low'], | |
| close=df['Close'], name='Price'), | |
| row=1, col=1) | |
| # Add Moving Averages | |
| fig.add_trace(go.Scatter(x=df.index, y=df['MA50'], | |
| name='MA50', line=dict(color='orange')), | |
| row=1, col=1) | |
| fig.add_trace(go.Scatter(x=df.index, y=df['MA200'], | |
| name='MA200', line=dict(color='blue')), | |
| row=1, col=1) | |
| # Add volume bars | |
| colors = ['red' if row['Open'] > row['Close'] else 'green' | |
| for index, row in df.iterrows()] | |
| fig.add_trace(go.Bar(x=df.index, y=df['Volume'], | |
| marker_color=colors, name='Volume'), | |
| row=2, col=1) | |
| fig.update_layout(height=800) | |
| st.plotly_chart(fig, use_container_width=True) | |
| with tab2: | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| # RSI Plot | |
| fig_rsi = go.Figure() | |
| fig_rsi.add_trace(go.Scatter(x=df.index, y=df['RSI'], | |
| name='RSI')) | |
| fig_rsi.add_hline(y=70, line_dash="dash", line_color="red") | |
| fig_rsi.add_hline(y=30, line_dash="dash", line_color="green") | |
| fig_rsi.update_layout(title='RSI Indicator') | |
| st.plotly_chart(fig_rsi) | |
| # MACD Plot | |
| fig_macd = go.Figure() | |
| fig_macd.add_trace(go.Scatter(x=df.index, y=df['MACD'], | |
| name='MACD')) | |
| fig_macd.add_trace(go.Scatter(x=df.index, y=df['MACD_Signal'], | |
| name='Signal')) | |
| fig_macd.update_layout(title='MACD Indicator') | |
| st.plotly_chart(fig_macd) | |
| # EMA Plot | |
| fig_ema = go.Figure() | |
| fig_ema.add_trace(go.Scatter(x=df.index, y=df['EMA9'], name='EMA9')) | |
| fig_ema.add_trace(go.Scatter(x=df.index, y=df['EMA21'], name='EMA21')) | |
| fig_ema.update_layout(title='EMA Indicators') | |
| st.plotly_chart(fig_ema) | |
| # OBV Plot | |
| fig_obv = go.Figure() | |
| fig_obv.add_trace(go.Scatter(x=df.index, y=df['OBV'], name='OBV')) | |
| fig_obv.update_layout(title='On-Balance Volume (OBV)') | |
| st.plotly_chart(fig_obv) | |
| # VWAP Plot | |
| fig_vwap = go.Figure() | |
| fig_vwap.add_trace(go.Scatter(x=df.index, y=df['Close'], name='Price')) | |
| fig_vwap.add_trace(go.Scatter(x=df.index, y=df['VWAP'], | |
| name='VWAP', line=dict(color='orange'))) | |
| fig_vwap.update_layout(title='Volume Weighted Average Price (VWAP)') | |
| st.plotly_chart(fig_vwap) | |
| with col2: | |
| # Stochastic Plot | |
| fig_stoch = go.Figure() | |
| fig_stoch.add_trace(go.Scatter(x=df.index, y=df['%K'], | |
| name='%K')) | |
| fig_stoch.add_trace(go.Scatter(x=df.index, y=df['%D'], | |
| name='%D')) | |
| fig_stoch.update_layout(title='Stochastic Oscillator') | |
| st.plotly_chart(fig_stoch) | |
| # Bollinger Bands | |
| fig_bb = go.Figure() | |
| fig_bb.add_trace(go.Scatter(x=df.index, y=df['BB_upper'], | |
| name='Upper Band')) | |
| fig_bb.add_trace(go.Scatter(x=df.index, y=df['Close'], | |
| name='Price')) | |
| fig_bb.add_trace(go.Scatter(x=df.index, y=df['BB_lower'], | |
| name='Lower Band')) | |
| fig_bb.update_layout(title='Bollinger Bands') | |
| st.plotly_chart(fig_bb) | |
| # Ichimoku Cloud | |
| fig_ichimoku = go.Figure() | |
| fig_ichimoku.add_trace(go.Scatter(x=df.index, y=df['ichimoku_a'], name='Senkou Span A')) | |
| fig_ichimoku.add_trace(go.Scatter(x=df.index, y=df['ichimoku_b'], name='Senkou Span B')) | |
| fig_ichimoku.add_trace(go.Scatter(x=df.index, y=df['ichimoku_base'], name='Kijun-sen')) | |
| fig_ichimoku.add_trace(go.Scatter(x=df.index, y=df['ichimoku_conversion'], name='Tenkan-sen')) | |
| fig_ichimoku.update_layout(title='Ichimoku Cloud') | |
| st.plotly_chart(fig_ichimoku) | |
| # SMA Plot | |
| fig_sma = go.Figure() | |
| fig_sma.add_trace(go.Scatter(x=df.index, y=df['Close'], name='Price')) | |
| fig_sma.add_trace(go.Scatter(x=df.index, y=df['SMA20'], name='SMA20')) | |
| fig_sma.add_trace(go.Scatter(x=df.index, y=df['SMA50'], name='SMA50')) | |
| fig_sma.add_trace(go.Scatter(x=df.index, y=df['SMA100'], name='SMA100')) | |
| fig_sma.add_trace(go.Scatter(x=df.index, y=df['SMA200'], name='SMA200')) | |
| fig_sma.update_layout(title='Simple Moving Averages (SMA)') | |
| st.plotly_chart(fig_sma) | |
| # ATR Plot | |
| fig_atr = go.Figure() | |
| fig_atr.add_trace(go.Scatter(x=df.index, y=df['ATR'], name='ATR')) | |
| fig_atr.update_layout(title='Average True Range (ATR)') | |
| st.plotly_chart(fig_atr) | |
| with tab3: | |
| st.subheader('Technical Analysis') | |
| # Combine all signals | |
| signals = generate_recommendation(df) | |
| enhanced_signals = enhanced_recommendation(df) | |
| all_signals = signals + enhanced_signals | |
| # Count buy and sell signals | |
| buy_signals = len([s for s in all_signals if s[0] == 'BUY']) | |
| sell_signals = len([s for s in all_signals if s[0] == 'SELL']) | |
| # Display recommendation summary | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.metric("Buy Signals", buy_signals) | |
| with col2: | |
| st.metric("Sell Signals", sell_signals) | |
| with col3: | |
| overall_rec = "BUY" if buy_signals > sell_signals else "SELL" if sell_signals > buy_signals else "HOLD" | |
| if overall_rec == "BUY": | |
| st.success(f"Overall: {overall_rec} π") | |
| elif overall_rec == "SELL": | |
| st.error(f"Overall: {overall_rec} π") | |
| else: | |
| st.warning(f"Overall: {overall_rec} βοΈ") | |
| # Display detailed signals | |
| st.subheader("Detailed Signals:") | |
| for signal, reason in all_signals: | |
| if signal == 'BUY': | |
| st.success(f"π’ {signal}: {reason}") | |
| else: | |
| st.error(f"π΄ {signal}: {reason}") | |
| # Technical Indicators Summary | |
| st.subheader("Technical Indicators Summary") | |
| # Create expandable section for current price levels | |
| with st.expander("π Current Price Levels", expanded=True): | |
| current_price = df['Close'].iloc[-1] | |
| prev_close = df['Close'].iloc[-2] | |
| price_change = ((current_price - prev_close) / prev_close) * 100 | |
| # Price information with colored indicators | |
| if price_change > 0: | |
| st.success(f"π Current Price: ${current_price:.2f} (+{price_change:.2f}%)") | |
| else: | |
| st.error(f"π Current Price: ${current_price:.2f} ({price_change:.2f}%)") | |
| # Display technical levels in a more organized way | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.info(f"πΉ VWAP\n${df['VWAP'].iloc[-1]:.2f}") | |
| with col2: | |
| st.info(f"π ATR\n${df['ATR'].iloc[-1]:.2f}") | |
| with col3: | |
| st.info(f"π SMA50\n${df['SMA50'].iloc[-1]:.2f}") | |
| # Moving Averages Analysis | |
| with st.expander("π Moving Averages Analysis", expanded=True): | |
| sma_status = "Bullish" if (df['SMA20'].iloc[-1] > df['SMA50'].iloc[-1]) else "Bearish" | |
| sma_icon = "π’" if sma_status == "Bullish" else "π΄" | |
| st.write(f"{sma_icon} SMA20 vs SMA50: {sma_status}") | |
| # Add more SMA comparisons | |
| sma_100_status = "Bullish" if (df['Close'].iloc[-1] > df['SMA100'].iloc[-1]) else "Bearish" | |
| sma_100_icon = "π’" if sma_100_status == "Bullish" else "π΄" | |
| st.write(f"{sma_100_icon} Price vs SMA100: {sma_100_status}") | |
| sma_200_status = "Bullish" if (df['Close'].iloc[-1] > df['SMA200'].iloc[-1]) else "Bearish" | |
| sma_200_icon = "π’" if sma_200_status == "Bullish" else "π΄" | |
| st.write(f"{sma_200_icon} Price vs SMA200: {sma_200_status}") | |
| # Volatility Analysis | |
| with st.expander("π Volatility Analysis", expanded=True): | |
| atr_avg = df['ATR'].mean() | |
| current_atr = df['ATR'].iloc[-1] | |
| atr_ratio = current_atr / atr_avg | |
| if atr_ratio > 1.5: | |
| volatility = "High" | |
| vol_icon = "β οΈ" | |
| st.warning(f"{vol_icon} Volatility: {volatility}") | |
| elif atr_ratio < 0.5: | |
| volatility = "Low" | |
| vol_icon = "π€" | |
| st.info(f"{vol_icon} Volatility: {volatility}") | |
| else: | |
| volatility = "Normal" | |
| vol_icon = "β " | |
| st.success(f"{vol_icon} Volatility: {volatility}") | |
| st.write(f"ATR Ratio: {atr_ratio:.2f}x average") | |
| # VWAP Analysis | |
| with st.expander("πΉ VWAP Analysis", expanded=True): | |
| vwap_diff = ((df['Close'].iloc[-1] - df['VWAP'].iloc[-1]) / df['VWAP'].iloc[-1]) * 100 | |
| if vwap_diff > 0: | |
| st.success(f"π’ Price is ABOVE VWAP by {abs(vwap_diff):.2f}%") | |
| else: | |
| st.error(f"π΄ Price is BELOW VWAP by {abs(vwap_diff):.2f}%") | |
| # Add risk warning | |
| st.warning("β οΈ Disclaimer: Above analysis is for AI Research and Learning purposes only. DO NOT make any investment decisions according to the displayed information and analysis result.") | |
| else: | |
| st.error('Error fetching stock data. Please check the symbol.') | |
| else: | |
| st.warning('Please enter a stock symbol.') | |
| with st.sidebar: | |
| # Add a horizontal line | |
| st.markdown("---") | |
| # Add text at the bottom | |
| st.warning("β οΈ Disclaimer: This is for AI Research and Learning purposes only. DO NOT make any investment decisions according to the displayed information and analysis result.") | |
| st.markdown("---") | |
| st.write("https://www.facebook.com/AIsparking") |