|
import streamlit as st |
|
from func import ( |
|
get_sentiment_pipeline, |
|
get_ner_pipeline, |
|
fetch_news, |
|
analyze_sentiment, |
|
extract_org_entities, |
|
) |
|
import time |
|
|
|
|
|
st.set_page_config( |
|
page_title="EquiPulse: Stock Sentiment Tracker", |
|
layout="centered", |
|
initial_sidebar_state="collapsed" |
|
) |
|
|
|
|
|
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) |
|
|
|
|
|
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) |
|
|
|
|
|
|
|
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) |
|
|
|
|
|
st.markdown("#### π― Enter Your Target Company Tickers") |
|
free_text = st.text_area("Example: AAPL, NVDA, TSLA", height=90) |
|
|
|
|
|
ner_pipeline = get_ner_pipeline() |
|
tickers = extract_org_entities(free_text, ner_pipeline) |
|
|
|
if tickers: |
|
st.markdown(f"β
**Recognized Tickers:** `{', '.join(tickers)}`") |
|
else: |
|
tickers = [] |
|
|
|
|
|
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 |
|
|
|
|
|
if pos_ratio >= 0.25: |
|
overall = "Positive" |
|
elif neg_ratio >= 0.75: |
|
overall = "Negative" |
|
else: |
|
overall = "Neutral" |
|
|
|
|
|
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) |
|
|