added app.py
Browse files
app.py
ADDED
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import requests
|
3 |
+
import re
|
4 |
+
import torch
|
5 |
+
from transformers import BertTokenizer, BertForSequenceClassification
|
6 |
+
|
7 |
+
# Load tokenizer and model
|
8 |
+
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
|
9 |
+
model_path = '/Users/kartikrathi/Documents/News_sentiment_analysis/model'
|
10 |
+
model = BertForSequenceClassification.from_pretrained(model_path)
|
11 |
+
|
12 |
+
# Function to analyze sentiment
|
13 |
+
def analyze_sentiment(text):
|
14 |
+
# Tokenize inputs
|
15 |
+
inputs = tokenizer(text, return_tensors="pt", truncation=True, padding=True, max_length=512)
|
16 |
+
|
17 |
+
# Perform inference
|
18 |
+
with torch.no_grad():
|
19 |
+
outputs = model(**inputs)
|
20 |
+
|
21 |
+
# Get predicted logits
|
22 |
+
logits = outputs.logits
|
23 |
+
|
24 |
+
# Determine predicted sentiment
|
25 |
+
predicted_class_id = torch.argmax(logits, dim=1).item()
|
26 |
+
sentiment = {0: "positive", 1: "negative", 2: "neutral"}
|
27 |
+
return sentiment[predicted_class_id]
|
28 |
+
|
29 |
+
# Function to fetch news using an API
|
30 |
+
def fetch_stock_news(symbol):
|
31 |
+
api_token = '6679177763bcd9.48465511' # Replace with your actual API token
|
32 |
+
url = f'https://eodhd.com/api/news?s={symbol}&offset=0&limit=2&api_token={api_token}&fmt=json'
|
33 |
+
|
34 |
+
try:
|
35 |
+
response = requests.get(url)
|
36 |
+
response.raise_for_status() # Raise exception for bad status codes
|
37 |
+
data = response.json()
|
38 |
+
|
39 |
+
news_list = []
|
40 |
+
for article in data:
|
41 |
+
title = article.get('title', 'Title not available')
|
42 |
+
content = article.get('content', 'Content not available')
|
43 |
+
url = article.get('url', 'URL not available')
|
44 |
+
date = article.get('date', 'Date not available')
|
45 |
+
|
46 |
+
# Remove unwanted sections from content
|
47 |
+
cleaned_content = remove_sections(content)
|
48 |
+
|
49 |
+
news_list.append({'title': title, 'content': cleaned_content, 'url': url, 'date': date})
|
50 |
+
|
51 |
+
return news_list
|
52 |
+
|
53 |
+
except requests.exceptions.RequestException as e:
|
54 |
+
print(f"Error fetching news for {symbol}: {e}")
|
55 |
+
return None
|
56 |
+
|
57 |
+
# Function to remove unwanted sections
|
58 |
+
def remove_sections(content):
|
59 |
+
# Patterns to remove
|
60 |
+
patterns = [
|
61 |
+
r'About\s+.*?[\n\r]+', # 'About {stock}' section
|
62 |
+
r'Safe\s+Harbor.*', # 'Safe Harbor' section
|
63 |
+
r'Story\s+continues.*', # 'Story continues'
|
64 |
+
r'Visit\s+www\.infosys\.com.*', # 'Visit www.infosys.com...'
|
65 |
+
r'Logo', # 'Logo'
|
66 |
+
]
|
67 |
+
|
68 |
+
for pattern in patterns:
|
69 |
+
content = re.sub(pattern, '', content, flags=re.IGNORECASE)
|
70 |
+
|
71 |
+
return content.strip()
|
72 |
+
|
73 |
+
# Streamlit app with multi-page support
|
74 |
+
def main():
|
75 |
+
st.sidebar.title('Navigation')
|
76 |
+
page = st.sidebar.radio("Go to", ('Home', 'Portfolio & News'))
|
77 |
+
|
78 |
+
if page == 'Home':
|
79 |
+
st.title("Financial News Sentiment Analysis")
|
80 |
+
st.markdown("""
|
81 |
+
This app performs sentiment analysis on financial news using FinBERT model.
|
82 |
+
""")
|
83 |
+
|
84 |
+
# Input text box for user input
|
85 |
+
user_input = st.text_area("Enter your financial news text here:", height=200)
|
86 |
+
|
87 |
+
# Perform sentiment analysis when user clicks the button
|
88 |
+
if st.button("Analyze"):
|
89 |
+
if user_input:
|
90 |
+
sentiment = analyze_sentiment(user_input)
|
91 |
+
if sentiment == "positive":
|
92 |
+
st.markdown(f"<p style='color:green;font-size:40px;font-weight:bold'>{sentiment}</p>", unsafe_allow_html=True)
|
93 |
+
elif sentiment == "negative":
|
94 |
+
st.markdown(f"<p style='color:red;font-size:40px;font-weight:bold'>{sentiment}</p>", unsafe_allow_html=True)
|
95 |
+
elif sentiment == "neutral":
|
96 |
+
st.markdown(f"<p style='color:blue;font-size:40px;font-weight:bold'>{sentiment}</p>", unsafe_allow_html=True)
|
97 |
+
else:
|
98 |
+
st.warning("Please enter some text.")
|
99 |
+
|
100 |
+
elif page == 'Portfolio & News':
|
101 |
+
st.title('Portfolio & News')
|
102 |
+
|
103 |
+
# Sidebar to manage portfolio and fetch news
|
104 |
+
st.sidebar.subheader('Manage Portfolio & Fetch News')
|
105 |
+
st.sidebar.info("Enter your portfolio/company names here to fetch news.")
|
106 |
+
|
107 |
+
# Input fields for portfolio management and news fetching
|
108 |
+
portfolio = st.sidebar.text_area("Enter your portfolio/company names (one per line):", height=200)
|
109 |
+
|
110 |
+
if st.sidebar.button("Save"):
|
111 |
+
# Add ".NSE" suffix to each company name
|
112 |
+
shares = portfolio.split("\n")
|
113 |
+
shares = [share.strip() + ".NSE" for share in shares if share.strip()]
|
114 |
+
st.sidebar.markdown("**Shares**")
|
115 |
+
st.sidebar.markdown("\n".join(shares))
|
116 |
+
|
117 |
+
# Button to fetch news and perform sentiment analysis
|
118 |
+
if st.sidebar.button("Fetch News & Analyze Sentiment"):
|
119 |
+
if portfolio:
|
120 |
+
companies = portfolio.split("\n")
|
121 |
+
companies = [company.strip() + ".NSE" for company in companies if company.strip()]
|
122 |
+
for company in companies:
|
123 |
+
st.subheader(f"Latest News for {company}:")
|
124 |
+
news = fetch_stock_news(company)
|
125 |
+
|
126 |
+
if news:
|
127 |
+
for article in news:
|
128 |
+
title = article['title']
|
129 |
+
content = article['content']
|
130 |
+
url = article['url']
|
131 |
+
date = article['date']
|
132 |
+
st.markdown(f"**Title:** [{title}]({url}) <span style='color:green;font-size:12px;'>({date})</span>", unsafe_allow_html=True)
|
133 |
+
|
134 |
+
# Display a truncated version of the content
|
135 |
+
truncated_content = content[:200] + "..."
|
136 |
+
st.markdown(f"**Content:** {truncated_content}")
|
137 |
+
|
138 |
+
# Expander for full content
|
139 |
+
with st.expander("Expand more"):
|
140 |
+
st.markdown(f"{content}")
|
141 |
+
|
142 |
+
st.markdown("---")
|
143 |
+
|
144 |
+
# Analyze sentiment of news content (assuming content is available)
|
145 |
+
if content:
|
146 |
+
sentiment = analyze_sentiment(content)
|
147 |
+
if sentiment == "positive":
|
148 |
+
sentiment_color = "green"
|
149 |
+
elif sentiment == "negative":
|
150 |
+
sentiment_color = "red"
|
151 |
+
elif sentiment == "neutral":
|
152 |
+
sentiment_color = "blue"
|
153 |
+
|
154 |
+
st.markdown(f"**Sentiment:** <span style='color:{sentiment_color};font-weight:bold'>{sentiment}</span>", unsafe_allow_html=True)
|
155 |
+
st.markdown("---")
|
156 |
+
else:
|
157 |
+
st.warning(f"No news articles found for {company}.")
|
158 |
+
else:
|
159 |
+
st.warning("Please enter portfolio/company names to fetch news.")
|
160 |
+
|
161 |
+
if __name__ == '__main__':
|
162 |
+
main()
|