Spaces:
Sleeping
Sleeping
import streamlit as st | |
from func import ( | |
get_sentiment_pipeline, | |
get_ner_pipeline, | |
fetch_news, | |
analyze_sentiment, | |
extract_org_entities, | |
) | |
import time | |
# ----------- Page Config ----------- | |
st.set_page_config( | |
page_title="EquiPulse: Stock Sentiment Tracker", | |
layout="centered", | |
initial_sidebar_state="collapsed" | |
) | |
# ----------- Custom Styling ----------- | |
st.markdown(""" | |
<style> | |
body { | |
background-color: #ffffff; | |
} | |
/* Title styling */ | |
.main-title { | |
font-size: 32px; | |
font-weight: 700; | |
font-family: 'Segoe UI', sans-serif; | |
color: #002b45; | |
margin-bottom: 1.2rem; | |
white-space: nowrap; | |
overflow-x: auto; | |
} | |
.main-title::-webkit-scrollbar { | |
height: 4px; | |
} | |
.main-title::-webkit-scrollbar-thumb { | |
background-color: #ccc; | |
border-radius: 2px; | |
} | |
/* Input + Button styling */ | |
.stTextInput > div > div > input, | |
.stTextArea textarea { | |
font-size: 16px; | |
} | |
.stButton>button { | |
background-color: #002b45; | |
color: white; | |
font-size: 16px; | |
padding: 0.4rem 1rem; | |
border-radius: 6px; | |
} | |
.stButton>button:hover { | |
background-color: #004b78; | |
} | |
.stMarkdown { | |
font-size: 16px; | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
# ----------- Header Title (Centered + Bold + Large + Professional) ----------- | |
st.markdown(""" | |
<style> | |
.centered-title { | |
text-align: center; | |
font-size: 36px; | |
font-weight: 800; | |
font-family: 'Segoe UI', sans-serif; | |
color: #002b45; | |
margin-top: 20px; | |
margin-bottom: 20px; | |
} | |
</style> | |
<div class="centered-title">π EquiPulse: Stock Sentiment Tracker</div> | |
""", unsafe_allow_html=True) | |
# ----------- Description Section ----------- | |
st.markdown(""" | |
<div style='font-size:16px; line-height:1.6; color: #333333; margin-bottom: 1rem;'> | |
Analyze real-time financial sentiment from news headlines related to companies you're interested in. | |
</div> | |
""", unsafe_allow_html=True) | |
# ----------- Input Area ----------- | |
st.markdown("#### π― Enter Your Target Company Tickers") | |
free_text = st.text_area("Example: AAPL, NVDA, TSLA", height=90) | |
# ----------- Ticker Extraction ----------- | |
ner_pipeline = get_ner_pipeline() | |
tickers = extract_org_entities(free_text, ner_pipeline) | |
if tickers: | |
st.markdown(f"β **Recognized Tickers:** `{', '.join(tickers)}`") | |
else: | |
tickers = [] | |
# ----------- Action Button ----------- | |
if st.button("π Get News and Sentiment"): | |
if not tickers: | |
st.warning("β οΈ Please enter at least one recognizable company name or ticker.") | |
else: | |
sentiment_pipeline = get_sentiment_pipeline() | |
progress_bar = st.progress(0) | |
total_stocks = len(tickers) | |
for idx, ticker in enumerate(tickers): | |
st.markdown(f"---\n#### π Analyzing `{ticker}`") | |
news_list = fetch_news(ticker) | |
if news_list: | |
sentiments = [analyze_sentiment(news['title'], sentiment_pipeline) for news in news_list] | |
pos_count = sentiments.count("Positive") | |
neg_count = sentiments.count("Negative") | |
total = len(sentiments) | |
pos_ratio = pos_count / total if total else 0 | |
neg_ratio = neg_count / total if total else 0 | |
# Simple heuristic for overall sentiment | |
if pos_ratio >= 0.25: | |
overall = "Positive" | |
elif neg_ratio >= 0.75: | |
overall = "Negative" | |
else: | |
overall = "Neutral" | |
# Display news | |
st.markdown(f"**π° Top News for `{ticker}`:**") | |
for i, news in enumerate(news_list[:3]): | |
st.markdown(f"{i+1}. [{news['title']}]({news['link']}) β **{sentiments[i]}**") | |
st.success(f"π **Overall Sentiment for `{ticker}`: {overall}**") | |
else: | |
st.info(f"No recent news found for `{ticker}`.") | |
progress_bar.progress((idx + 1) / total_stocks) | |
time.sleep(0.1) | |