Spaces:
Sleeping
Sleeping
import streamlit as st | |
import yfinance as yf | |
import pandas as pd | |
def get_sp500_list(): | |
table = pd.read_html('https://en.wikipedia.org/wiki/List_of_S%26P_500_companies') | |
return table[0]['Symbol'].tolist() | |
def load_sp500_averages(filepath): | |
return pd.read_csv(filepath, header=0, names=['Ratio', 'Average']).set_index('Ratio') | |
def fetch_stock_data(ticker_symbol): | |
ticker = yf.Ticker(ticker_symbol) | |
info = ticker.info | |
financials = { | |
'P/E Ratio': info.get('forwardPE'), | |
'P/B Ratio': info.get('priceToBook'), | |
'P/S Ratio': info.get('priceToSalesTrailing12Months'), | |
'Debt to Equity Ratio': info.get('debtToEquity'), | |
'Return on Equity': info.get('returnOnEquity'), | |
'Book-to-Market Ratio': 1 / info.get('priceToBook') if info.get('priceToBook') else None | |
} | |
# Debug: Print to see if financials are fetched correctly | |
print(f"Financials for {ticker_symbol}: {financials}") | |
return financials, info | |
def compare_to_index(stock_ratios, index_averages): | |
comparison = {} | |
score = 0 | |
for ratio, value in stock_ratios.items(): | |
if ratio in index_averages.index and pd.notna(value): | |
average = index_averages.loc[ratio, 'Average'] | |
comparison[ratio] = 'Undervalued' if value < average else 'Overvalued' | |
score += 1 if value < average else -1 | |
return comparison, score | |
def calculate_combined_scores_for_stocks(stocks, index_averages): | |
scores = [] | |
for ticker_symbol in stocks: | |
stock_data, _ = fetch_stock_data(ticker_symbol) | |
comparison, score = compare_to_index(stock_data, index_averages) | |
scores.append({'Stock': ticker_symbol, 'Combined Score': score}) | |
return pd.DataFrame(scores) | |
def color_combined_score(value): | |
if value > 0: | |
color = 'green' | |
elif value < 0: | |
color = 'red' | |
else: | |
color = 'lightgrey' | |
return f'background-color: {color};' | |
def filter_incomplete_stocks(df, required_columns): | |
# Ensure all required columns exist in the DataFrame | |
for column in required_columns: | |
if column not in df.columns: | |
df[column] = pd.NA | |
return df.dropna(subset=required_columns) | |
st.title('S&P 500 Stock Comparison Tool') | |
sp500_list = get_sp500_list() | |
sp500_averages = load_sp500_averages('sp500_averages.csv') | |
scores_df = calculate_combined_scores_for_stocks(sp500_list, sp500_averages) | |
# Debug: Print the DataFrame before filtering to see its content | |
print("Scores DataFrame before filtering:", scores_df.head()) | |
required_columns = ['P/E Ratio', 'P/B Ratio', 'P/S Ratio', 'Debt to Equity Ratio', 'Return on Equity', 'Book-to-Market Ratio'] | |
# Attempt a lenient filtering approach or skip filtering to debug | |
# scores_df_filtered = filter_incomplete_stocks(scores_df, required_columns) | |
scores_df_filtered = scores_df # Temporarily bypass filtering to debug | |
scores_df_sorted = scores_df_filtered.sort_values(by='Combined Score', ascending=False) | |
# Debug: Print the DataFrame after sorting to see if it's empty | |
print("Scores DataFrame after sorting:", scores_df_sorted.head()) | |
col1, col2 = st.columns([3, 5]) | |
with col1: | |
st.subheader("Stock Overview") | |
if not scores_df_sorted.empty: | |
styled_scores_df = scores_df_sorted.style.applymap(color_combined_score, subset=['Combined Score']) | |
st.dataframe(styled_scores_df) | |
else: | |
st.write("No data available after filtering.") | |
with col2: | |
st.subheader("Stock Details") | |
if not scores_df_sorted.empty: | |
sorted_tickers = scores_df_sorted['Stock'].tolist() | |
ticker_symbol = st.selectbox('Select a stock for details', options=sorted_tickers) | |
if ticker_symbol: | |
with st.spinner(f'Fetching data for {ticker_symbol}...'): | |
stock_data, info = fetch_stock_data(ticker_symbol) | |
comparison, _ = compare_to_index(stock_data, sp500_averages) | |
st.write(f"**{info.get('longName', 'N/A')}** ({ticker_symbol})") | |
st.write(info.get('longBusinessSummary', 'N/A')) | |
for ratio in required_columns: | |
value = stock_data.get(ratio, 'N/A') | |
average = sp500_averages.loc[ratio, 'Average'] if ratio in sp500_averages.index else 'N/A' | |
status = comparison.get(ratio, 'N/A') | |
st.write(f"{ratio}: {value} (Your Ratio) | {average} (S&P 500 Avg) - {status}") | |
else: | |
st.write("No stocks to display.") |