Spaces:
Running
Running
| import pandas as pd | |
| import numpy as np | |
| import yfinance as yf | |
| import streamlit as st | |
| import plotly.graph_objects as go | |
| from plotly.subplots import make_subplots | |
| from ta.momentum import RSIIndicator, AwesomeOscillatorIndicator, WilliamsRIndicator | |
| from ta.trend import MACD | |
| from ta.volatility import BollingerBands | |
| # Helper functions | |
| def fetch_stock_data(ticker: str, start_date: str, end_date: str) -> pd.DataFrame: | |
| """Fetch stock or crypto data from Yahoo Finance.""" | |
| data = yf.download(ticker, start=start_date, end=end_date, auto_adjust=False) | |
| if isinstance(data.columns, pd.MultiIndex): | |
| data.columns = data.columns.get_level_values(0) | |
| return data | |
| def plot_z_score(close_prices: pd.Series, periods: list, z_thresh: float, n_days: int) -> go.Figure: | |
| fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1, | |
| subplot_titles=('Close Prices', 'Rolling Z-Scores')) | |
| # Plot stock price | |
| fig.add_trace(go.Scatter(x=close_prices.index, y=close_prices, mode='lines', name='Close Prices'), row=1, col=1) | |
| for period in periods: | |
| rolling_mean = close_prices.rolling(window=period).mean() | |
| rolling_std = close_prices.rolling(window=period).std() | |
| z_scores = (close_prices - rolling_mean) / rolling_std | |
| buy_signals = z_scores < -z_thresh | |
| sell_signals = z_scores > z_thresh | |
| future_prices = close_prices.shift(-n_days) | |
| correct_buy_signals = (buy_signals & (close_prices < future_prices)).sum() | |
| total_buy_signals = buy_signals.sum() | |
| # Use scalar values for the conditional | |
| buy_correct_pct = (correct_buy_signals / total_buy_signals * 100) if total_buy_signals > 0 else 0 | |
| correct_sell_signals = (sell_signals & (close_prices > future_prices)).sum() | |
| total_sell_signals = sell_signals.sum() | |
| # Use scalar values for the conditional | |
| sell_correct_pct = (correct_sell_signals / total_sell_signals * 100) if total_sell_signals > 0 else 0 | |
| fig.add_trace(go.Scatter(x=close_prices[buy_signals].index, y=close_prices[buy_signals], | |
| mode='markers', marker=dict(color='green', symbol='triangle-up', size=10), | |
| name=f'Buy Signal {period} days'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=close_prices[sell_signals].index, y=close_prices[sell_signals], | |
| mode='markers', marker=dict(color='red', symbol='triangle-down', size=10), | |
| name=f'Sell Signal {period} days'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=close_prices.index, y=z_scores, mode='lines', name=f'Rolling Z-Score {period}'), row=2, col=1) | |
| # Add threshold lines | |
| fig.add_hline(y=z_thresh, line=dict(color='red', dash='dash'), row=2, col=1) | |
| fig.add_hline(y=-z_thresh, line=dict(color='green', dash='dash'), row=2, col=1) | |
| fig.update_layout(title=f'{ticker} Close Prices and Rolling Z-Scores', xaxis_title='Date', yaxis_title='Price', yaxis2_title='Rolling Z-Score') | |
| return fig | |
| def plot_roc(prices: pd.Series, n: int = 14) -> go.Figure: | |
| roc = (prices - prices.shift(n)) / prices.shift(n) * 100 | |
| fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1, | |
| subplot_titles=('Stock Price', 'Rate of Change (ROC)')) | |
| # Plot stock price | |
| fig.add_trace(go.Scatter(x=prices.index, y=prices, mode='lines', name='Stock Price'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=prices.index, y=roc, mode='lines', name='ROC'), row=2, col=1) | |
| buy_signals = (roc.shift(1) <= 0) & (roc > 0) | |
| sell_signals = (roc.shift(1) >= 0) & (roc < 0) | |
| fig.add_trace(go.Scatter(x=prices[buy_signals].index, y=prices[buy_signals], mode='markers', | |
| marker=dict(color='blue', symbol='triangle-up', size=10), name='Buy Signal'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=prices[sell_signals].index, y=prices[sell_signals], mode='markers', | |
| marker=dict(color='red', symbol='triangle-down', size=10), name='Sell Signal'), row=1, col=1) | |
| # Add threshold line | |
| fig.add_hline(y=0, line=dict(color='gray', dash='dash'), row=2, col=1) | |
| fig.update_layout(title='Rate of Change (ROC) with Buy/Sell Signals', xaxis_title='Date', yaxis_title='Price', yaxis2_title='ROC') | |
| return fig | |
| def plot_stochastic_oscillator(data: pd.DataFrame, buy_thresh: float, sell_thresh: float) -> go.Figure: | |
| high14 = data['High'].rolling(14).max() | |
| low14 = data['Low'].rolling(14).min() | |
| data['%K'] = (data['Close'] - low14) * 100 / (high14 - low14) | |
| data['%D'] = data['%K'].rolling(3).mean() | |
| buy_signals = data['%K'] < buy_thresh | |
| sell_signals = data['%K'] > sell_thresh | |
| fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1, | |
| subplot_titles=('Close Price', 'Stochastic Oscillator')) | |
| fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=data.index, y=data['%K'], mode='lines', name='%K'), row=2, col=1) | |
| fig.add_trace(go.Scatter(x=data.index, y=data['%D'], mode='lines', name='%D'), row=2, col=1) | |
| fig.add_trace(go.Scatter(x=data[buy_signals].index, y=data[buy_signals]['Close'], mode='markers', | |
| marker=dict(color='green', symbol='triangle-up', size=10), name='Buy Signal'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=data[sell_signals].index, y=data[sell_signals]['Close'], mode='markers', | |
| marker=dict(color='red', symbol='triangle-down', size=10), name='Sell Signal'), row=1, col=1) | |
| # Add threshold lines | |
| fig.add_hline(y=buy_thresh, line=dict(color='green', dash='dash'), row=2, col=1) | |
| fig.add_hline(y=sell_thresh, line=dict(color='red', dash='dash'), row=2, col=1) | |
| fig.update_layout(title='Stochastic Oscillator with Buy/Sell Signals', xaxis_title='Date', yaxis_title='Price', yaxis2_title='%K and %D') | |
| return fig | |
| def plot_rsi(prices: pd.Series, buy_thresh: float, sell_thresh: float) -> go.Figure: | |
| rsi = RSIIndicator(prices).rsi() | |
| fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1, | |
| subplot_titles=('Stock Price', 'RSI')) | |
| fig.add_trace(go.Scatter(x=prices.index, y=prices, mode='lines', name='Stock Price'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=prices.index, y=rsi, mode='lines', name='RSI'), row=2, col=1) | |
| buy_signals = (rsi < buy_thresh) | |
| sell_signals = (rsi > sell_thresh) | |
| fig.add_trace(go.Scatter(x=prices[buy_signals].index, y=prices[buy_signals], mode='markers', | |
| marker=dict(color='blue', symbol='triangle-up', size=10), name='Buy Signal'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=prices[sell_signals].index, y=prices[sell_signals], mode='markers', | |
| marker=dict(color='red', symbol='triangle-down', size=10), name='Sell Signal'), row=1, col=1) | |
| # Add threshold lines | |
| fig.add_hline(y=buy_thresh, line=dict(color='green', dash='dash'), row=2, col=1) | |
| fig.add_hline(y=sell_thresh, line=dict(color='red', dash='dash'), row=2, col=1) | |
| fig.update_layout(title='RSI with Buy/Sell Signals', xaxis_title='Date', yaxis_title='Price', yaxis2_title='RSI') | |
| return fig | |
| def plot_macd(prices: pd.Series) -> go.Figure: | |
| macd = MACD(prices) | |
| macd_line = macd.macd() | |
| signal_line = macd.macd_signal() | |
| macd_hist = macd.macd_diff() | |
| fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1, | |
| subplot_titles=('Stock Price', 'MACD')) | |
| fig.add_trace(go.Scatter(x=prices.index, y=prices, mode='lines', name='Stock Price'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=prices.index, y=macd_line, mode='lines', name='MACD'), row=2, col=1) | |
| fig.add_trace(go.Scatter(x=prices.index, y=signal_line, mode='lines', name='Signal Line'), row=2, col=1) | |
| fig.add_trace(go.Bar(x=prices.index, y=macd_hist, name='MACD Histogram'), row=2, col=1) | |
| buy_signals = (macd_line > signal_line) & (macd_line.shift(1) <= signal_line.shift(1)) | |
| sell_signals = (macd_line < signal_line) & (macd_line.shift(1) >= signal_line.shift(1)) | |
| fig.add_trace(go.Scatter(x=prices[buy_signals].index, y=prices[buy_signals], mode='markers', | |
| marker=dict(color='blue', symbol='triangle-up', size=10), name='Buy Signal'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=prices[sell_signals].index, y=prices[sell_signals], mode='markers', | |
| marker=dict(color='red', symbol='triangle-down', size=10), name='Sell Signal'), row=1, col=1) | |
| fig.update_layout(title='MACD with Buy/Sell Signals', xaxis_title='Date', yaxis_title='Price', yaxis2_title='MACD') | |
| return fig | |
| def plot_bollinger_bands(prices: pd.Series) -> go.Figure: | |
| bollinger = BollingerBands(prices) | |
| upper_band = bollinger.bollinger_hband() | |
| lower_band = bollinger.bollinger_lband() | |
| fig = go.Figure() | |
| fig.add_trace(go.Scatter(x=prices.index, y=prices, mode='lines', name='Stock Price')) | |
| fig.add_trace(go.Scatter(x=prices.index, y=upper_band, mode='lines', name='Upper Band')) | |
| fig.add_trace(go.Scatter(x=prices.index, y=lower_band, mode='lines', name='Lower Band')) | |
| buy_signals = prices < lower_band | |
| sell_signals = prices > upper_band | |
| fig.add_trace(go.Scatter(x=prices[buy_signals].index, y=prices[buy_signals], mode='markers', | |
| marker=dict(color='blue', symbol='triangle-up', size=10), name='Buy Signal')) | |
| fig.add_trace(go.Scatter(x=prices[sell_signals].index, y=prices[sell_signals], mode='markers', | |
| marker=dict(color='red', symbol='triangle-down', size=10), name='Sell Signal')) | |
| fig.update_layout(title='Bollinger Bands with Buy/Sell Signals', xaxis_title='Date', yaxis_title='Price') | |
| return fig | |
| def plot_k_reversal(data: pd.DataFrame, n: int, buy_thresh: float, sell_thresh: float) -> go.Figure: | |
| """Plot K Reversal indicator with buy and sell signals.""" | |
| high_n = data['High'].rolling(n).max() | |
| low_n = data['Low'].rolling(n).min() | |
| k_values = 100 * (data['Close'] - low_n) / (high_n - low_n) | |
| fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1, | |
| subplot_titles=('Close Price', 'K Reversal')) | |
| fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=data.index, y=k_values, mode='lines', name='K Reversal'), row=2, col=1) | |
| buy_signals = k_values < buy_thresh | |
| sell_signals = k_values > sell_thresh | |
| fig.add_trace(go.Scatter(x=data[buy_signals].index, y=data[buy_signals]['Close'], mode='markers', | |
| marker=dict(color='green', symbol='triangle-up', size=10), name='Buy Signal'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=data[sell_signals].index, y=data[sell_signals]['Close'], mode='markers', | |
| marker=dict(color='red', symbol='triangle-down', size=10), name='Sell Signal'), row=1, col=1) | |
| # Add threshold lines | |
| fig.add_hline(y=buy_thresh, line=dict(color='green', dash='dash'), row=2, col=1) | |
| fig.add_hline(y=sell_thresh, line=dict(color='red', dash='dash'), row=2, col=1) | |
| fig.update_layout(title='K Reversal with Buy/Sell Signals', xaxis_title='Date', yaxis_title='Price', yaxis2_title='K Reversal') | |
| return fig | |
| def plot_awesome_oscillator(data: pd.DataFrame, signal_period: int, buy_thresh: float, sell_thresh: float) -> go.Figure: | |
| """Plot Awesome Oscillator with buy and sell signals.""" | |
| ao_indicator = AwesomeOscillatorIndicator(high=data['High'], low=data['Low']) | |
| data['AO'] = ao_indicator.awesome_oscillator() | |
| data['Signal'] = data['AO'].rolling(window=signal_period).mean() | |
| fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1, | |
| subplot_titles=('Close Price', 'Awesome Oscillator')) | |
| fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price'), row=1, col=1) | |
| fig.add_trace(go.Bar(x=data.index, y=data['AO'] - data['Signal'], name='AO Histogram', marker_color='blue'), row=2, col=1) | |
| fig.add_trace(go.Scatter(x=data.index, y=data['Signal'], mode='lines', name='Signal Line', line=dict(color='orange')), row=2, col=1) | |
| buy_signals = (data['AO'] > buy_thresh) & (data['AO'].shift(1) <= buy_thresh) | |
| sell_signals = (data['AO'] < sell_thresh) & (data['AO'].shift(1) >= sell_thresh) | |
| fig.add_trace(go.Scatter(x=data[buy_signals].index, y=data['Close'][buy_signals], mode='markers', | |
| marker=dict(color='green', symbol='triangle-up', size=10), name='Buy Signal'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=data[sell_signals].index, y=data['Close'][sell_signals], mode='markers', | |
| marker=dict(color='red', symbol='triangle-down', size=10), name='Sell Signal'), row=1, col=1) | |
| # Add threshold lines | |
| fig.add_hline(y=buy_thresh, line=dict(color='green', dash='dash'), row=2, col=1) | |
| fig.add_hline(y=sell_thresh, line=dict(color='red', dash='dash'), row=2, col=1) | |
| fig.update_layout(title='Awesome Oscillator with Buy/Sell Signals', xaxis_title='Date', yaxis_title='Price', yaxis2_title='AO') | |
| return fig | |
| def plot_williams_r(data: pd.DataFrame, n: int, buy_thresh: float, sell_thresh: float) -> go.Figure: | |
| """Plot Williams %R with buy and sell signals.""" | |
| williams_r = WilliamsRIndicator(high=data['High'], low=data['Low'], close=data['Close'], lbp=n) | |
| data['Williams %R'] = williams_r.williams_r() | |
| fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1, | |
| subplot_titles=('Close Price', 'Williams %R')) | |
| fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=data.index, y=data['Williams %R'], mode='lines', name='Williams %R'), row=2, col=1) | |
| buy_signals = (data['Williams %R'] > buy_thresh) & (data['Williams %R'].shift(1) <= buy_thresh) | |
| sell_signals = (data['Williams %R'] < sell_thresh) & (data['Williams %R'].shift(1) >= sell_thresh) | |
| fig.add_trace(go.Scatter(x=data[buy_signals].index, y=data[buy_signals]['Close'], mode='markers', | |
| marker=dict(color='green', symbol='triangle-up', size=10), name='Buy Signal'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=data[sell_signals].index, y=data[sell_signals]['Close'], mode='markers', | |
| marker=dict(color='red', symbol='triangle-down', size=10), name='Sell Signal'), row=1, col=1) | |
| # Add threshold lines | |
| fig.add_hline(y=buy_thresh, line=dict(color='green', dash='dash'), row=2, col=1) | |
| fig.add_hline(y=sell_thresh, line=dict(color='red', dash='dash'), row=2, col=1) | |
| fig.update_layout(title='Williams %R with Buy/Sell Signals', xaxis_title='Date', yaxis_title='Price', yaxis2_title='Williams %R') | |
| return fig | |
| def plot_aroon(data: pd.DataFrame, aroon_osc: pd.Series, buy_signals: pd.Series, sell_signals: pd.Series) -> go.Figure: | |
| """Plot Aroon Oscillator with buy and sell signals.""" | |
| fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1, | |
| subplot_titles=('Close Price', 'Aroon Oscillator')) | |
| fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close Price'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=data.index, y=aroon_osc, mode='lines', name='Aroon Oscillator', line=dict(color='purple')), row=2, col=1) | |
| fig.add_trace(go.Scatter(x=buy_signals.index[buy_signals], y=data['Close'][buy_signals], mode='markers', | |
| marker=dict(color='green', symbol='triangle-up', size=10), name='Buy Signal'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=sell_signals.index[sell_signals], y=data['Close'][sell_signals], mode='markers', | |
| marker=dict(color='red', symbol='triangle-down', size=10), name='Sell Signal'), row=1, col=1) | |
| # Add threshold line | |
| fig.add_hline(y=0, line=dict(color='black', dash='dash'), row=2, col=1) | |
| fig.update_layout(title='Aroon Oscillator with Buy/Sell Signals', xaxis_title='Date', yaxis_title='Price', yaxis2_title='Aroon Oscillator') | |
| return fig | |
| def aroon_oscillator(data: pd.DataFrame, period: int) -> pd.Series: | |
| """Calculate Aroon Oscillator.""" | |
| aroon_up = 100 * (data['High'].rolling(period + 1).apply(lambda x: np.argmax(x[::-1]), raw=True) / period) | |
| aroon_down = 100 * (data['Low'].rolling(period + 1).apply(lambda x: np.argmin(x[::-1]), raw=True) / period) | |
| aroon_osc = aroon_up - aroon_down | |
| return aroon_osc | |
| # Streamlit app | |
| st.set_page_config(page_title="Technical Analysis", layout="wide") | |
| st.title('Technical Analysis Indicators') | |
| st.sidebar.title('Input Parameters') | |
| # Sidebar for method selection | |
| with st.sidebar.expander("Method Selection", expanded=True): | |
| selected = st.radio("Select Indicator", ["Rolling Z-Score", "Rate of Change (ROC)", "Stochastic Oscillator", "Relative Strength Index (RSI)", "MACD", "Bollinger Bands", "K Reversal", "Awesome Oscillator", "Williams %R", "Aroon Oscillator"]) | |
| # Sidebar for "How to Use" instructions specific to the selected method | |
| with st.sidebar.expander("How to Use", expanded=False): | |
| if selected == "Rolling Z-Score": | |
| st.markdown(""" | |
| 1. Enter the stock or crypto symbol (e.g., 'AAPL' for Apple or 'BTC-USD' for Bitcoin). | |
| 2. Choose the date range. | |
| 3. Set the number of days for the z-score calculation. | |
| 4. Set the z-score threshold. | |
| 5. Click 'Fetch Data' to load the data. | |
| 6. The chart will display the rolling z-scores and highlight buy/sell signals based on the thresholds. | |
| """) | |
| elif selected == "Rate of Change (ROC)": | |
| st.markdown(""" | |
| 1. Enter the stock or crypto symbol. | |
| 2. Choose the date range. | |
| 3. Set the number of days for the ROC calculation. | |
| 4. Click 'Fetch Data' to load the data. | |
| 5. The chart will display the ROC and highlight buy/sell signals based on ROC crossing zero. | |
| """) | |
| elif selected == "Stochastic Oscillator": | |
| st.markdown(""" | |
| 1. Enter the stock or crypto symbol. | |
| 2. Choose the date range. | |
| 3. Set the buy and sell thresholds. | |
| 4. Click 'Fetch Data' to load the data. | |
| 5. The chart will display the Stochastic Oscillator and highlight buy/sell signals. | |
| """) | |
| elif selected == "Relative Strength Index (RSI)": | |
| st.markdown(""" | |
| 1. Enter the stock or crypto symbol. | |
| 2. Choose the date range. | |
| 3. Set the buy and sell thresholds. | |
| 4. Click 'Fetch Data' to load the data. | |
| 5. The chart will display the RSI and highlight buy/sell signals. | |
| """) | |
| elif selected == "MACD": | |
| st.markdown(""" | |
| 1. Enter the stock or crypto symbol. | |
| 2. Choose the date range. | |
| 3. Click 'Fetch Data' to load the data. | |
| 4. The chart will display the MACD, Signal line, and Histogram, and highlight buy/sell signals based on MACD crossovers. | |
| """) | |
| elif selected == "Bollinger Bands": | |
| st.markdown(""" | |
| 1. Enter the stock or crypto symbol. | |
| 2. Choose the date range. | |
| 3. Click 'Fetch Data' to load the data. | |
| 4. The chart will display the Bollinger Bands and highlight buy/sell signals based on price crossing the bands. | |
| """) | |
| elif selected == "K Reversal": | |
| st.markdown(""" | |
| 1. Enter the stock or crypto symbol. | |
| 2. Choose the date range. | |
| 3. Set the period for the K Reversal calculation. | |
| 4. Set the buy and sell thresholds. | |
| 5. Click 'Fetch Data' to load the data. | |
| 6. The chart will display the K Reversal indicator and highlight buy/sell signals. | |
| """) | |
| elif selected == "Awesome Oscillator": | |
| st.markdown(""" | |
| 1. Enter the stock or crypto symbol. | |
| 2. Choose the date range. | |
| 3. Set the signal line period. | |
| 4. Set the buy and sell thresholds. | |
| 5. Click 'Fetch Data' to load the data. | |
| 6. The chart will display the Awesome Oscillator and highlight buy/sell signals. | |
| """) | |
| elif selected == "Williams %R": | |
| st.markdown(""" | |
| 1. Enter the stock or crypto symbol. | |
| 2. Choose the date range. | |
| 3. Set the look-back period. | |
| 4. Set the buy and sell thresholds. | |
| 5. Click 'Fetch Data' to load the data. | |
| 6. The chart will display the Williams %R and highlight buy/sell signals. | |
| """) | |
| elif selected == "Aroon Oscillator": | |
| st.markdown(""" | |
| 1. Enter the stock or crypto symbol. | |
| 2. Choose the date range. | |
| 3. Set the look-back period. | |
| 4. Click 'Fetch Data' to load the data. | |
| 5. The chart will display the Aroon Oscillator and highlight buy/sell signals based on thresholds. | |
| """) | |
| # Sidebar for input parameters inside an expander | |
| with st.sidebar.expander("Input Parameters", expanded=True): | |
| ticker = st.text_input('Enter Stock or Crypto Symbol (e.g., AAPL or BTC-USD)', 'AAPL', help="Enter the ticker symbol for the stock or cryptocurrency you want to analyze.") | |
| start_date = st.date_input('Start Date', pd.to_datetime('2019-01-01'), help="Select the start date for the data range.") | |
| end_date = st.date_input('End Date', pd.to_datetime(pd.Timestamp.now().date() + pd.Timedelta(days=1)), help="Select the end date for the data range.") | |
| # Fetch data | |
| if 'data' not in st.session_state or st.sidebar.button('Fetch Data'): | |
| data = fetch_stock_data(ticker, start_date, end_date) | |
| if data.empty: | |
| st.error(f"No data returned for {ticker} from {start_date} to {end_date}") | |
| else: | |
| st.session_state.data = data | |
| if 'data' in st.session_state and not st.session_state.data.empty: | |
| data = st.session_state.data | |
| close_prices = data['Close'] | |
| # Display results based on the selected method | |
| if selected == "Rolling Z-Score": | |
| st.markdown("## Rolling Z-Score") | |
| st.markdown("The rolling z-score method identifies overbought and oversold conditions by standardizing stock prices over different periods.") | |
| with st.expander("Formula and Method Description", expanded=False): | |
| st.markdown(""" | |
| **How it Works:** | |
| 1. **Calculate Rolling Mean and Standard Deviation:** | |
| - For each time period (e.g., 20 days), compute the rolling mean and rolling standard deviation of the stock prices. | |
| 2. **Compute Z-Score:** | |
| - For each day, calculate the z-score using: | |
| """) | |
| st.latex(r''' | |
| \text{Z-Score} = \frac{\text{Close Price} - \text{Rolling Mean}}{\text{Rolling Standard Deviation}} | |
| ''') | |
| st.markdown(""" | |
| 3. **Identify Signals:** | |
| - **Overbought Condition:** Z-score above a set threshold (e.g., 2.0) suggests the stock may be overbought. | |
| - **Oversold Condition:** Z-score below a set threshold (e.g., -2.0) suggests the stock may be oversold. | |
| """) | |
| periods = st.sidebar.multiselect('Periods to Compare', options=[10, 20, 30, 40, 50], default=[20], help="Select multiple periods to compare the rolling z-scores.") | |
| z_thresh = st.sidebar.slider('Z-Score Threshold', min_value=0.5, max_value=3.0, value=2.0, step=0.1, help="Set the z-score threshold for identifying overbought and oversold conditions.") | |
| n_days = st.sidebar.number_input('Number of Days', min_value=1, value=10, help="Set the number of days to shift for future price comparison.") | |
| fig = plot_z_score(close_prices, periods, z_thresh, n_days) | |
| st.plotly_chart(fig) | |
| elif selected == "Rate of Change (ROC)": | |
| st.markdown("## Rate of Change (ROC)") | |
| st.markdown("The Rate of Change (ROC) method measures the percentage change in stock prices over a specified period. It helps identify momentum and potential reversal points in the stock price.") | |
| with st.expander("Formula and Method Description", expanded=False): | |
| st.markdown(""" | |
| **How it Works:** | |
| 1. **Calculate ROC:** | |
| - For each day, calculate the ROC using the formula: | |
| """) | |
| st.latex(r''' | |
| \text{ROC} = \frac{\text{Current Price} - \text{Price} \, n \, \text{days ago}}{\text{Price} \, n \, \text{days ago}} \times 100 | |
| ''') | |
| st.markdown(""" | |
| 2. **Identify Signals:** | |
| - **Buy Signal:** Occurs when the ROC crosses above zero, indicating potential upward momentum. | |
| - **Sell Signal:** Occurs when the ROC crosses below zero, indicating potential downward momentum. | |
| """) | |
| n_days = st.sidebar.number_input('Number of Days', min_value=1, value=14, help="Set the number of days for the ROC calculation.") | |
| fig = plot_roc(close_prices, n_days) | |
| st.plotly_chart(fig) | |
| elif selected == "Stochastic Oscillator": | |
| st.markdown("## Stochastic Oscillator") | |
| st.markdown("The Stochastic Oscillator compares a stock's closing price to its price range over a specified period. It helps identify overbought and oversold conditions.") | |
| with st.expander("Formula and Method Description", expanded=False): | |
| st.markdown(""" | |
| **How it Works:** | |
| 1. **Calculate %K and %D:** | |
| - The %K line is calculated as follows: | |
| """) | |
| st.latex(r''' | |
| \%K = \frac{\text{Current Close} - \text{Lowest Low}}{\text{Highest High} - \text{Lowest Low}} \times 100 | |
| ''') | |
| st.markdown(""" | |
| - The %D line is the 3-period moving average of %K. | |
| 2. **Identify Signals:** | |
| - **Buy Signal:** Occurs when %K crosses above a set threshold (e.g., 20), indicating potential upward momentum. | |
| - **Sell Signal:** Occurs when %K crosses below a set threshold (e.g., 80), indicating potential downward momentum. | |
| """) | |
| buy_thresh = st.sidebar.slider('Stochastic Buy Threshold', min_value=0, max_value=100, value=5, help="Set the threshold for generating buy signals using the Stochastic Oscillator.") | |
| sell_thresh = st.sidebar.slider('Stochastic Sell Threshold', min_value=0, max_value=100, value=95, help="Set the threshold for generating sell signals using the Stochastic Oscillator.") | |
| fig = plot_stochastic_oscillator(data, buy_thresh, sell_thresh) | |
| st.plotly_chart(fig) | |
| elif selected == "Relative Strength Index (RSI)": | |
| st.markdown("## Relative Strength Index (RSI)") | |
| st.markdown("The RSI measures the magnitude of recent price changes to evaluate overbought or oversold conditions.") | |
| with st.expander("Formula and Method Description", expanded=False): | |
| st.markdown(""" | |
| **How it Works:** | |
| 1. **Calculate RSI:** | |
| - Compute the average gains and average losses over a specified period (typically 14 days). | |
| - Calculate the RSI using the formula: | |
| """) | |
| st.latex(r''' | |
| \text{RSI} = 100 - \frac{100}{1 + \frac{\text{Average Gain}}{\text{Average Loss}}} | |
| ''') | |
| st.markdown(""" | |
| 2. **Identify Signals:** | |
| - **Buy Signal:** Occurs when the RSI crosses below a set threshold (e.g., 30), indicating the stock may be oversold. | |
| - **Sell Signal:** Occurs when the RSI crosses above a set threshold (e.g., 70), indicating the stock may be overbought. | |
| """) | |
| buy_thresh = st.sidebar.slider('RSI Buy Threshold', min_value=0, max_value=100, value=30, help="Set the RSI threshold for generating buy signals.") | |
| sell_thresh = st.sidebar.slider('RSI Sell Threshold', min_value=0, max_value=100, value=70, help="Set the RSI threshold for generating sell signals.") | |
| fig = plot_rsi(close_prices, buy_thresh, sell_thresh) | |
| st.plotly_chart(fig) | |
| elif selected == "MACD": | |
| st.markdown("## Moving Average Convergence Divergence (MACD)") | |
| st.markdown("The MACD is a trend-following momentum indicator that shows the relationship between two moving averages of a stock's price.") | |
| with st.expander("Formula and Method Description", expanded=False): | |
| st.markdown(""" | |
| **How it Works:** | |
| 1. **Calculate MACD:** | |
| - Compute the MACD line using the formula: | |
| """) | |
| st.latex(r''' | |
| \text{MACD} = \text{EMA}_{12} - \text{EMA}_{26} | |
| ''') | |
| st.markdown(""" | |
| - Compute the Signal line as the 9-day EMA of the MACD line. | |
| - The MACD Histogram is the difference between the MACD line and the Signal line. | |
| 2. **Identify Signals:** | |
| - **Buy Signal:** Occurs when the MACD line crosses above the Signal line, indicating potential upward momentum. | |
| - **Sell Signal:** Occurs when the MACD line crosses below the Signal line, indicating potential downward momentum. | |
| """) | |
| fig = plot_macd(close_prices) | |
| st.plotly_chart(fig) | |
| elif selected == "Bollinger Bands": | |
| st.markdown("## Bollinger Bands") | |
| st.markdown("Bollinger Bands consist of a middle band (Simple Moving Average) and two outer bands (standard deviations from the SMA). They help identify overbought and oversold conditions.") | |
| with st.expander("Formula and Method Description", expanded=False): | |
| st.markdown(""" | |
| **How it Works:** | |
| 1. **Calculate Bollinger Bands:** | |
| - Compute the middle band as the Simple Moving Average (SMA) of the stock prices over a specified period (typically 20 days). | |
| - Calculate the upper and lower bands using the formulas: | |
| """) | |
| st.latex(r''' | |
| \text{Upper Band} = \text{SMA} + k \cdot \text{Standard Deviation} | |
| ''') | |
| st.latex(r''' | |
| \text{Lower Band} = \text{SMA} - k \cdot \text{Standard Deviation} | |
| ''') | |
| st.markdown(""" | |
| - Where \( k \) is a factor typically set to 2. | |
| 2. **Identify Signals:** | |
| - **Buy Signal:** Occurs when the stock price crosses below the lower band, indicating the stock may be oversold. | |
| - **Sell Signal:** Occurs when the stock price crosses above the upper band, indicating the stock may be overbought. | |
| """) | |
| fig = plot_bollinger_bands(close_prices) | |
| st.plotly_chart(fig) | |
| elif selected == "K Reversal": | |
| st.markdown("## K Reversal") | |
| st.markdown("The K Reversal indicator helps identify potential reversal points based on the stock's high and low prices over a specified period.") | |
| with st.expander("Formula and Method Description", expanded=False): | |
| st.markdown(""" | |
| **How it Works:** | |
| 1. **Calculate K Reversal:** | |
| - Compute the highest high and lowest low over a specified period (e.g., 14 days). | |
| - Calculate the K value using the formula: | |
| """) | |
| st.latex(r''' | |
| K = \frac{\text{Close Price} - \text{Lowest Low}}{\text{Highest High} - \text{Lowest Low}} \times 100 | |
| ''') | |
| st.markdown(""" | |
| 2. **Identify Signals:** | |
| - **Buy Signal:** Occurs when the K value crosses below a set threshold (e.g., 20), indicating the stock may be oversold. | |
| - **Sell Signal:** Occurs when the K value crosses above a set threshold (e.g., 80), indicating the stock may be overbought. | |
| """) | |
| k_period = st.sidebar.number_input('K Reversal Period', min_value=1, value=14, help="Set the period for calculating the K Reversal.") | |
| k_buy_thresh = st.sidebar.slider('K Reversal Buy Threshold', min_value=0.0, max_value=100.0, value=10.0, help="Set the threshold for generating buy signals using the K Reversal indicator.") | |
| k_sell_thresh = st.sidebar.slider('K Reversal Sell Threshold', min_value=0.0, max_value=100.0, value=90.0, help="Set the threshold for generating sell signals using the K Reversal indicator.") | |
| fig = plot_k_reversal(data, k_period, k_buy_thresh, k_sell_thresh) | |
| st.plotly_chart(fig) | |
| elif selected == "Awesome Oscillator": | |
| st.markdown("## Awesome Oscillator") | |
| st.markdown("The Awesome Oscillator measures market momentum by comparing the 34-period and 5-period simple moving averages of the midpoints of each candlestick.") | |
| with st.expander("Formula and Method Description", expanded=False): | |
| st.markdown(""" | |
| **How it Works:** | |
| 1. **Calculate the Midpoint:** | |
| - For each candlestick, compute the midpoint using: | |
| """) | |
| st.latex(r''' | |
| \text{Midpoint} = \frac{\text{High} + \text{Low}}{2} | |
| ''') | |
| st.markdown(""" | |
| 2. **Compute the Moving Averages:** | |
| - Calculate the 34-period and 5-period simple moving averages (SMA) of the midpoints. | |
| 3. **Calculate the Awesome Oscillator:** | |
| - Use the formula: | |
| """) | |
| st.latex(r''' | |
| \text{AO} = \text{SMA}_{5}(\text{Midpoint}) - \text{SMA}_{34}(\text{Midpoint}) | |
| ''') | |
| st.markdown(""" | |
| 4. **Identify Signals:** | |
| - **Buy Signal:** Occurs when the AO crosses above the signal line (e.g., 0), indicating potential upward momentum. | |
| - **Sell Signal:** Occurs when the AO crosses below the signal line, indicating potential downward momentum. | |
| """) | |
| signal_period = st.sidebar.number_input('Signal Line Period', min_value=1, value=9, help="Set the period for the signal line in the Awesome Oscillator.") | |
| ao_buy_thresh = st.sidebar.slider('AO Buy Threshold', min_value=-100.0, max_value=100.0, value=0.0, help="Set the threshold for generating buy signals using the Awesome Oscillator.") | |
| ao_sell_thresh = st.sidebar.slider('AO Sell Threshold', min_value=-100.0, max_value=100.0, value=0.0, help="Set the threshold for generating sell signals using the Awesome Oscillator.") | |
| fig = plot_awesome_oscillator(data, signal_period, ao_buy_thresh, ao_sell_thresh) | |
| st.plotly_chart(fig) | |
| elif selected == "Williams %R": | |
| st.markdown("## Williams %R") | |
| st.markdown("The Williams %R measures overbought and oversold levels.") | |
| with st.expander("Formula and Method Description", expanded=False): | |
| st.markdown(""" | |
| **How it Works:** | |
| 1. **Calculate Williams %R:** | |
| - Compute the highest high and lowest low over a specified look-back period (e.g., 14 days). | |
| - Calculate the Williams %R using the formula: | |
| """) | |
| st.latex(r''' | |
| \text{Williams \%R} = \frac{\text{Highest High} - \text{Close}}{\text{Highest High} - \text{Lowest Low}} \times -100 | |
| ''') | |
| st.markdown(""" | |
| 2. **Identify Signals:** | |
| - **Buy Signal:** Occurs when the Williams %R crosses below a set threshold (e.g., -80), indicating the stock may be oversold. | |
| - **Sell Signal:** Occurs when the Williams %R crosses above a set threshold (e.g., -20), indicating the stock may be overbought. | |
| """) | |
| williams_r_period = st.sidebar.number_input('Look-back Period', min_value=1, value=14, help="Set the look-back period for the Williams %R calculation.") | |
| williams_r_buy_thresh = st.sidebar.slider('Williams %R Buy Threshold', min_value=-100.0, max_value=0.0, value=-90.0, help="Set the threshold for generating buy signals using Williams %R.") | |
| williams_r_sell_thresh = st.sidebar.slider('Williams %R Sell Threshold', min_value=-100.0, max_value=0.0, value=-10.0, help="Set the threshold for generating sell signals using Williams %R.") | |
| fig = plot_williams_r(data, williams_r_period, williams_r_buy_thresh, williams_r_sell_thresh) | |
| st.plotly_chart(fig) | |
| elif selected == "Aroon Oscillator": | |
| st.markdown("## Aroon Oscillator") | |
| st.markdown("The Aroon Oscillator measures the strength of a trend in the stock's price.") | |
| with st.expander("Formula and Method Description", expanded=False): | |
| st.markdown(""" | |
| **How it Works:** | |
| 1. **Calculate Aroon Up and Aroon Down:** | |
| - Compute the Aroon Up, which measures the time since the highest high during the look-back period: | |
| """) | |
| st.latex(r''' | |
| \text{Aroon Up} = \frac{N - \text{Days Since Highest High}}{N} \times 100 | |
| ''') | |
| st.markdown(""" | |
| - Compute the Aroon Down, which measures the time since the lowest low during the look-back period: | |
| """) | |
| st.latex(r''' | |
| \text{Aroon Down} = \frac{N - \text{Days Since Lowest Low}}{N} \times 100 | |
| ''') | |
| st.markdown(""" | |
| 2. **Calculate the Aroon Oscillator:** | |
| - Use the formula: | |
| """) | |
| st.latex(r''' | |
| \text{Aroon Oscillator} = \text{Aroon Up} - \text{Aroon Down} | |
| ''') | |
| st.markdown(""" | |
| 3. **Identify Signals:** | |
| - **Buy Signal:** Occurs when the Aroon Oscillator crosses above zero, indicating a potential upward trend. | |
| - **Sell Signal:** Occurs when the Aroon Oscillator crosses below zero, indicating a potential downward trend. | |
| """) | |
| aroon_period = st.sidebar.number_input('Look-back Period', min_value=1, value=25, help="Set the look-back period for the Aroon Oscillator calculation.") | |
| aroon_osc = aroon_oscillator(data, aroon_period) | |
| buy_signals = (aroon_osc > 0) & (aroon_osc.shift(1) <= 0) | |
| sell_signals = (aroon_osc < 0) & (aroon_osc.shift(1) >= 0) | |
| fig = plot_aroon(data, aroon_osc, buy_signals, sell_signals) | |
| st.plotly_chart(fig) | |
| # Hide the default Streamlit menu and footer | |
| hide_streamlit_style = """ | |
| <style> | |
| #MainMenu {visibility: hidden;} | |
| footer {visibility: hidden;} | |
| </style> | |
| """ | |
| st.markdown(hide_streamlit_style, unsafe_allow_html=True) |