| import streamlit as st |
| import pandas as pd |
| import yfinance as yf |
|
|
| |
| df = pd.read_csv('merged_data.csv') |
| overview = pd.read_csv('company_overview.csv') |
|
|
| st.image('napol.png') |
| st.write('Hi! This is Napol, your long term personal portfolio assistant. See S&P 500 stock current prices and buy/sell signals by Short Term (3 Months), Long Term (18 Months) and Analyst Rating (Performance Forecasts). Press the buttons below to sort stocks.') |
|
|
|
|
| def score_to_percentage(score): |
| percentage = abs(score) * 100 |
| if score > 0: |
| status = "buy" |
| elif score < 0: |
| status = "sell" |
| else: |
| status = "" |
| return f"{percentage:.0f}%{f' {status}' if status else ''}" |
|
|
|
|
| |
| def get_color(percentage): |
| |
| scaled_intensity = 0.30 + 0.70 * (abs(percentage) / 100) |
| if percentage == 0: |
| return "gray" |
| elif percentage > 0: |
| return f"rgba(19, 204, 78, {scaled_intensity})" |
| else: |
| return f"rgba(212, 0, 40, {scaled_intensity})" |
|
|
|
|
| |
| def get_stock_info(ticker): |
| try: |
| stock = yf.Ticker(ticker) |
| info = stock.info |
| |
| |
| if 'regularMarketPrice' in info: |
| current_price = info['regularMarketPrice'] |
| elif 'currentPrice' in info: |
| current_price = info['currentPrice'] |
| else: |
| current_price = stock.history(period="1d")['Close'].iloc[-1] |
| |
| |
| hist = stock.history(period="5d") |
| if not hist.empty: |
| weekly_change = ((hist['Close'].iloc[-1] - hist['Open'].iloc[0]) / hist['Open'].iloc[0]) * 100 |
| else: |
| weekly_change = 0 |
| |
| return current_price, weekly_change |
| except Exception as e: |
| print(f"Error fetching data for {ticker}: {str(e)}") |
| return None, None |
|
|
| |
| df = pd.merge(df, overview[['Symbol', 'Name']], left_on='symbol', right_on='Symbol') |
|
|
| df = df[df['symbol'] != 'GS'] |
|
|
| |
| df['total_score'] = df['short_term'] + df['long_term'] + df['analyst_rating'] |
|
|
| |
| if 'sort_column' not in st.session_state: |
| st.session_state.sort_column = 'total_score' |
| st.session_state.ascending = False |
|
|
| |
| sort_options = { |
| "Total Buy": ("total_score", False), |
| "Total Sell": ("total_score", True), |
| "Long Term Buy": ("long_term", False), |
| "Long Term Sell": ("long_term", True), |
| "Analyst Rating Buy": ("analyst_rating", False), |
| "Analyst Rating Sell": ("analyst_rating", True), |
| "Short Term Buy": ("short_term", False), |
| "Short Term Sell": ("short_term", True), |
|
|
| } |
|
|
| |
| cols = st.columns(4) |
| for i, (option, (column, ascending)) in enumerate(sort_options.items()): |
| if cols[i % 4].button(option): |
| st.session_state.sort_column = column |
| st.session_state.ascending = ascending |
|
|
| |
| num_stocks = st.slider("Select number of stocks to display:", min_value=1, max_value=100, value=25) |
|
|
| |
| df_sorted = df.sort_values(st.session_state.sort_column, ascending=st.session_state.ascending).head(num_stocks) |
|
|
| |
| for _, row in df_sorted.iterrows(): |
| ticker = row['symbol'] |
| |
| |
| current_price, weekly_change = get_stock_info(ticker) |
| |
| if current_price is None or weekly_change is None: |
| continue |
|
|
| |
| with open(f'images/{ticker}.svg', 'r') as f: svg_code = f.read() |
| svg_code = f"""<style>svg {{border-radius: 10px; height: 20px; width: 20px; border: 1px solid #202020; margin-right: 10px;}}</style>{svg_code}""" |
| |
| |
| html_content = f""" |
| <div style="background-color: #101010; border: 1px solid #202020; padding: 10px; margin-bottom: 10px; border-radius: 6px; width: 100%; box-sizing: border-box;"> |
| <div style="display: flex; justify-content: space-between; align-items: center;"> |
| <div style="display: flex; align-items: center;"> |
| {svg_code} |
| <span style="font-weight: bold; color: white; font-size: 18px;">{ticker}</span> |
| </div> |
| <span style="font-size: 18px; color: white;">${current_price:.2f}</span> |
| </div> |
| <div style="display: flex; justify-content: space-between; margin-top: 2px;"> |
| <span style="color: #bfbfbf;">{row['Name']}</span> |
| <span style="color: {'green' if weekly_change > 0 else 'red'}"> |
| week ({'+' if weekly_change > 0 else ''}{weekly_change:.2f}%) |
| </span> |
| </div> |
| <div style="display: flex; justify-content: space-between; margin-top: 10px;"> |
| <!-- Short Term Box --> |
| <div style="text-align: center; width: 32%; padding: 8px; border: 1px solid #202020; border-radius: 4px; box-sizing: border-box;"> |
| <div style="color: white; font-size: 14px; font-weight: bold;">Short Term</div> |
| <div style="color: {get_color((row['short_term']) * 100)};"> |
| {score_to_percentage(row['short_term'])} |
| </div> |
| </div> |
| <!-- Long Term Box --> |
| <div style="text-align: center; width: 32%; padding: 8px; border: 1px solid #202020; border-radius: 4px; box-sizing: border-box;"> |
| <div style="color: white; font-size: 14px; font-weight: bold;">Long Term</div> |
| <div style="color: {get_color((row['long_term']) * 100)};"> |
| {score_to_percentage(row['long_term'])} |
| </div> |
| </div> |
| <!-- Analyst Rating Box --> |
| <div style="text-align: center; width: 32%; padding: 8px; border: 1px solid #202020; border-radius: 4px; box-sizing: border-box;"> |
| <div style="color: white; font-size: 14px; font-weight: bold;">Analyst Rate</div> |
| <div style="color: {get_color((row['analyst_rating']) * 100)};"> |
| {score_to_percentage(row['analyst_rating'])} |
| </div> |
| </div> |
| </div> |
| </div> |
| """ |
| st.markdown(html_content, unsafe_allow_html=True) |
|
|
|
|