news_sentiment_analyzer / src /streamlit_app.py
ResearchEngineering's picture
Update src/streamlit_app.py
ddebd3a verified
raw
history blame
43.3 kB
'''
import altair as alt
import numpy as np
import pandas as pd
import streamlit as st
"""
# Welcome to Streamlit!
Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
forums](https://discuss.streamlit.io).
In the meantime, below is an example of what you can do with just a few lines of code:
"""
num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
indices = np.linspace(0, 1, num_points)
theta = 2 * np.pi * num_turns * indices
radius = indices
x = radius * np.cos(theta)
y = radius * np.sin(theta)
df = pd.DataFrame({
"x": x,
"y": y,
"idx": indices,
"rand": np.random.randn(num_points),
})
st.altair_chart(alt.Chart(df, height=700, width=700)
.mark_point(filled=True)
.encode(
x=alt.X("x", axis=None),
y=alt.Y("y", axis=None),
color=alt.Color("idx", legend=None, scale=alt.Scale()),
size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
))
import streamlit as st
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
import torch.nn.functional as F
import os
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime
import re
# Page configuration
st.set_page_config(
page_title="FinBERT Sentiment Analyzer",
page_icon="πŸ’°",
layout="wide",
initial_sidebar_state="expanded"
)
# Custom CSS for better styling
st.markdown("""
<style>
.main-header {
text-align: center;
color: #1f77b4;
margin-bottom: 2rem;
}
.sentiment-card {
padding: 1rem;
border-radius: 10px;
margin: 0.5rem 0;
text-align: center;
}
.negative { background-color: #ffebee; border-left: 5px solid #f44336; }
.neutral { background-color: #f3e5f5; border-left: 5px solid #9c27b0; }
.positive { background-color: #e8f5e8; border-left: 5px solid #4caf50; }
.metric-container {
background-color: #f8f9fa;
padding: 1rem;
border-radius: 10px;
margin: 1rem 0;
}
</style>
""", unsafe_allow_html=True)
st.markdown('<h1 class="main-header">πŸ’° FinBERT: Financial Sentiment Analysis</h1>', unsafe_allow_html=True)
# Sidebar
with st.sidebar:
st.header("ℹ️ About")
st.markdown("""
**Model:** `yiyanghkust/finbert-tone`
Trained specifically on financial texts for accurate sentiment analysis of:
- Financial news
- Earnings reports
- Market analysis
- Investment research
""")
st.header("βš™οΈ Settings")
confidence_threshold = st.slider("Confidence Threshold", 0.0, 1.0, 0.5, help="Minimum confidence for sentiment classification")
show_probabilities = st.checkbox("Show All Probabilities", value=True)
batch_analysis = st.checkbox("Enable Batch Analysis", help="Analyze multiple texts at once")
@st.cache_resource(show_spinner=False)
def load_model():
"""Load FinBERT model and tokenizer with error handling"""
try:
cache_dir = "/tmp/huggingface"
os.makedirs(cache_dir, exist_ok=True)
with st.spinner("Loading FinBERT model... This may take a moment."):
tokenizer = AutoTokenizer.from_pretrained(
"yiyanghkust/finbert-tone",
cache_dir=cache_dir
)
model = AutoModelForSequenceClassification.from_pretrained(
"yiyanghkust/finbert-tone",
cache_dir=cache_dir
)
return tokenizer, model, None
except Exception as e:
return None, None, str(e)
def analyze_sentiment(text, tokenizer, model):
"""Analyze sentiment with error handling and additional metrics"""
try:
# Preprocess text
text = re.sub(r'\s+', ' ', text.strip())
inputs = tokenizer(
text,
return_tensors="pt",
truncation=True,
padding=True,
max_length=512
)
with torch.no_grad():
outputs = model(**inputs)
probs = F.softmax(outputs.logits, dim=1).squeeze()
labels = ["Negative", "Neutral", "Positive"]
sentiment_scores = {label: prob.item() for label, prob in zip(labels, probs)}
# Determine primary sentiment
max_prob = max(sentiment_scores.values())
primary_sentiment = max(sentiment_scores, key=sentiment_scores.get)
return sentiment_scores, primary_sentiment, max_prob, None
except Exception as e:
return None, None, None, str(e)
def create_sentiment_chart(sentiment_scores):
"""Create an interactive sentiment visualization"""
labels = list(sentiment_scores.keys())
values = list(sentiment_scores.values())
colors = ['#f44336', '#9c27b0', '#4caf50']
fig = go.Figure(data=[
go.Bar(
x=labels,
y=values,
marker_color=colors,
text=[f'{v:.3f}' for v in values],
textposition='auto',
)
])
fig.update_layout(
title="Sentiment Analysis Results",
xaxis_title="Sentiment",
yaxis_title="Confidence Score",
yaxis=dict(range=[0, 1]),
height=400,
showlegend=False
)
return fig
# Load model
tokenizer, model, error = load_model()
if error:
st.error(f"Failed to load model: {error}")
st.stop()
if tokenizer and model:
st.success("βœ… FinBERT model loaded successfully!")
# Main analysis interface
if not batch_analysis:
st.header("πŸ“ Single Text Analysis")
text = st.text_area(
"Enter financial news, report, or analysis:",
height=150,
placeholder="Example: The company reported strong quarterly earnings with revenue growth of 15% year-over-year..."
)
col1, col2, col3 = st.columns([1, 1, 2])
with col1:
analyze_button = st.button("πŸ” Analyze Sentiment", type="primary")
with col2:
clear_button = st.button("πŸ—‘οΈ Clear")
if clear_button:
st.rerun()
if analyze_button and text.strip():
with st.spinner("Analyzing sentiment..."):
sentiment_scores, primary_sentiment, confidence, error = analyze_sentiment(text, tokenizer, model)
if error:
st.error(f"Analysis failed: {error}")
else:
# Results section
st.header("πŸ“Š Analysis Results")
# Primary sentiment with confidence
col1, col2, col3 = st.columns(3)
sentiment_emojis = {"Negative": "πŸ“‰", "Neutral": "😐", "Positive": "πŸ“ˆ"}
sentiment_colors = {"Negative": "red", "Neutral": "gray", "Positive": "green"}
with col1:
st.metric(
"Primary Sentiment",
f"{sentiment_emojis[primary_sentiment]} {primary_sentiment}",
delta=f"{confidence:.1%} confidence"
)
with col2:
st.metric(
"Text Length",
f"{len(text)} characters",
delta=f"{len(text.split())} words"
)
with col3:
reliability = "High" if confidence > 0.7 else "Medium" if confidence > 0.5 else "Low"
st.metric("Reliability", reliability)
# Detailed probabilities
if show_probabilities:
st.subheader("Detailed Sentiment Scores")
for sentiment, score in sentiment_scores.items():
emoji = sentiment_emojis[sentiment]
color = "negative" if sentiment == "Negative" else "neutral" if sentiment == "Neutral" else "positive"
st.markdown(f"""
<div class="sentiment-card {color}">
<h4>{emoji} {sentiment}</h4>
<h2>{score:.3f}</h2>
<div style="width: 100%; background-color: #ddd; border-radius: 25px;">
<div style="width: {score*100}%; height: 10px; background-color: {sentiment_colors[sentiment]}; border-radius: 25px;"></div>
</div>
</div>
""", unsafe_allow_html=True)
# Visualization
st.subheader("πŸ“ˆ Sentiment Visualization")
fig = create_sentiment_chart(sentiment_scores)
st.plotly_chart(fig, use_container_width=True)
else:
# Batch analysis mode
st.header("πŸ“Š Batch Analysis")
# Option to upload file or enter multiple texts
analysis_method = st.radio(
"Choose analysis method:",
["Enter multiple texts", "Upload CSV file"]
)
if analysis_method == "Enter multiple texts":
texts_input = st.text_area(
"Enter multiple texts (one per line):",
height=200,
placeholder="Text 1: Company reports strong earnings...\nText 2: Market volatility increases...\nText 3: New regulations impact sector..."
)
if st.button("πŸ” Analyze All Texts") and texts_input.strip():
texts = [text.strip() for text in texts_input.split('\n') if text.strip()]
if texts:
results = []
progress_bar = st.progress(0)
for i, text in enumerate(texts):
sentiment_scores, primary_sentiment, confidence, error = analyze_sentiment(text, tokenizer, model)
if not error:
results.append({
'Text': text[:100] + '...' if len(text) > 100 else text,
'Primary Sentiment': primary_sentiment,
'Confidence': confidence,
'Negative': sentiment_scores['Negative'],
'Neutral': sentiment_scores['Neutral'],
'Positive': sentiment_scores['Positive']
})
progress_bar.progress((i + 1) / len(texts))
if results:
df = pd.DataFrame(results)
# Summary statistics
st.subheader("πŸ“ˆ Batch Analysis Summary")
col1, col2, col3 = st.columns(3)
with col1:
positive_count = len(df[df['Primary Sentiment'] == 'Positive'])
st.metric("Positive Texts", positive_count, f"{positive_count/len(df)*100:.1f}%")
with col2:
neutral_count = len(df[df['Primary Sentiment'] == 'Neutral'])
st.metric("Neutral Texts", neutral_count, f"{neutral_count/len(df)*100:.1f}%")
with col3:
negative_count = len(df[df['Primary Sentiment'] == 'Negative'])
st.metric("Negative Texts", negative_count, f"{negative_count/len(df)*100:.1f}%")
# Results table
st.subheader("πŸ“‹ Detailed Results")
st.dataframe(df, use_container_width=True)
# Download results
csv = df.to_csv(index=False)
st.download_button(
"πŸ“₯ Download Results (CSV)",
csv,
f"sentiment_analysis_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
"text/csv"
)
elif analysis_method == "Upload CSV file":
uploaded_file = st.file_uploader(
"Choose a CSV file with a 'text' column",
type=['csv']
)
if uploaded_file is not None:
try:
df = pd.read_csv(uploaded_file)
if 'text' not in df.columns:
st.error("CSV file must contain a 'text' column")
else:
st.write(f"Loaded {len(df)} texts from CSV file")
st.dataframe(df.head(), use_container_width=True)
if st.button("πŸ” Analyze CSV Data"):
results = []
progress_bar = st.progress(0)
for i, row in df.iterrows():
text = str(row['text'])
sentiment_scores, primary_sentiment, confidence, error = analyze_sentiment(text, tokenizer, model)
if not error:
result_row = row.to_dict()
result_row.update({
'Primary Sentiment': primary_sentiment,
'Confidence': confidence,
'Negative Score': sentiment_scores['Negative'],
'Neutral Score': sentiment_scores['Neutral'],
'Positive Score': sentiment_scores['Positive']
})
results.append(result_row)
progress_bar.progress((i + 1) / len(df))
if results:
results_df = pd.DataFrame(results)
# Display results
st.subheader("πŸ“‹ Analysis Results")
st.dataframe(results_df, use_container_width=True)
# Download enhanced results
csv = results_df.to_csv(index=False)
st.download_button(
"πŸ“₯ Download Enhanced Results (CSV)",
csv,
f"enhanced_sentiment_analysis_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
"text/csv"
)
except Exception as e:
st.error(f"Error processing CSV file: {str(e)}")
# Footer
st.markdown("---")
st.markdown("""
<div style='text-align: center; color: #666; margin-top: 2rem;'>
<p>πŸ’‘ <strong>Tip:</strong> For best results, use complete sentences and financial context</p>
<p>Built with Streamlit β€’ Powered by FinBERT</p>
</div>
""", unsafe_allow_html=True)
import streamlit as st
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
import torch.nn.functional as F
import os
st.set_page_config(page_title="πŸ’° FinBERT: Financial Sentiment Analysis", layout="centered")
st.title("πŸ’° FinBERT: Financial Sentiment Analysis")
st.markdown("МодСль: `yiyanghkust/finbert-tone` β€” ΠΎΠ±ΡƒΡ‡Π΅Π½Π° Π½Π° финансовых тСкстах")
@st.cache_resource
def load_model():
# Установка кастомного ΠΏΡƒΡ‚ΠΈ ΠΊ ΠΊΡΡˆΡƒ
cache_dir = "/tmp/huggingface"
os.makedirs(cache_dir, exist_ok=True)
tokenizer = AutoTokenizer.from_pretrained("yiyanghkust/finbert-tone", cache_dir=cache_dir)
model = AutoModelForSequenceClassification.from_pretrained("yiyanghkust/finbert-tone", cache_dir=cache_dir)
return tokenizer, model
tokenizer, model = load_model()
text = st.text_area("Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Ρ„ΠΈΠ½Π°Π½ΡΠΎΠ²ΡƒΡŽ Π½ΠΎΠ²ΠΎΡΡ‚ΡŒ ΠΈΠ»ΠΈ ΠΎΡ‚Ρ‡Ρ‘Ρ‚:", height=150)
if st.button("ΠΠ½Π°Π»ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ‚ΠΎΠ½Π°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ") and text.strip():
inputs = tokenizer(text, return_tensors="pt", truncation=True, padding=True)
with torch.no_grad():
outputs = model(**inputs)
probs = F.softmax(outputs.logits, dim=1).squeeze()
labels = ["πŸ“‰ Negative", "😐 Neutral", "πŸ“ˆ Positive"]
for label, prob in zip(labels, probs):
st.write(f"**{label}:** {prob.item():.3f}")
'''
import time
import os
from datetime import datetime, timedelta
import re
import yfinance as yf
import streamlit as st
from transformers import AutoTokenizer, AutoModelForSequenceClassification, AutoModelForTokenClassification, pipeline
import torch
import torch.nn.functional as F
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
from textblob import TextBlob
import requests
from bs4 import BeautifulSoup
# Page configuration
st.set_page_config(
page_title="Financial News Sentiment Analyzer",
page_icon="πŸ“ˆ",
layout="wide",
initial_sidebar_state="expanded"
)
# Custom CSS for financial theme
st.markdown("""
<style>
.main-header {
text-align: center;
background: linear-gradient(90deg, #1f4e79, #2e7d32);
color: white;
padding: 1rem;
border-radius: 15px;
margin-bottom: 2rem;
}
.metric-card {
background: white;
padding: 1.5rem;
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
border-left: 4px solid #1f4e79;
margin: 1rem 0;
}
.bullish { border-left-color: #4caf50 !important; }
.bearish { border-left-color: #f44336 !important; }
.neutral { border-left-color: #ff9800 !important; }
.market-impact {
padding: 1rem;
border-radius: 8px;
margin: 0.5rem 0;
font-weight: bold;
}
.high-impact { background-color: #ffebee; color: #c62828; }
.medium-impact { background-color: #fff3e0; color: #ef6c00; }
.low-impact { background-color: #e8f5e8; color: #2e7d32; }
.trading-signal {
padding: 1rem;
border-radius: 10px;
text-align: center;
font-size: 1.2rem;
font-weight: bold;
margin: 1rem 0;
}
.buy-signal { background: linear-gradient(135deg, #4caf50, #66bb6a); color: white; }
.sell-signal { background: linear-gradient(135deg, #f44336, #ef5350); color: white; }
.hold-signal { background: linear-gradient(135deg, #ff9800, #ffa726); color: white; }
.risk-indicator {
display: inline-block;
padding: 0.3rem 0.8rem;
border-radius: 20px;
font-size: 0.9rem;
font-weight: bold;
margin: 0.2rem;
}
.risk-low { background-color: #4caf50; color: white; }
.risk-medium { background-color: #ff9800; color: white; }
.risk-high { background-color: #f44336; color: white; }
</style>
""", unsafe_allow_html=True)
st.markdown('<div class="main-header"><h1>πŸ“ˆ Financial News Sentiment Analysis Platform</h1><p>AI-Powered Market Intelligence & Trading Insights</p></div>', unsafe_allow_html=True)
# Sidebar configuration
with st.sidebar:
st.header("🎯 Analysis Configuration")
analysis_type = st.selectbox(
"Analysis Type:",
["Single News Analysis", "Portfolio Impact Analysis", "Market Sector Analysis", "Real-time News Feed"]
)
st.header("πŸ“Š Financial Models")
model_choice = st.selectbox(
"Sentiment Model:",
["FinBERT (Financial)", "RoBERTa (General)", "Custom Ensemble"]
)
st.header("βš™οΈ Trading Parameters")
risk_tolerance = st.selectbox("Risk Tolerance:", ["Conservative", "Moderate", "Aggressive"])
investment_horizon = st.selectbox("Investment Horizon:", ["Day Trading", "Swing (1-7 days)", "Position (1-3 months)", "Long-term (6+ months)"])
position_size = st.slider("Position Size ($)", 1000, 100000, 10000, 1000)
st.header("πŸŽ›οΈ Alert Settings")
sentiment_threshold = st.slider("Sentiment Alert Threshold", 0.0, 1.0, 0.7)
enable_notifications = st.checkbox("Enable Trading Alerts")
@st.cache_resource
def load_financial_models():
"""Load multiple financial sentiment models"""
try:
cache_dir = "/tmp/huggingface"
os.makedirs(cache_dir, exist_ok=True)
# FinBERT for financial sentiment
finbert_tokenizer = AutoTokenizer.from_pretrained("yiyanghkust/finbert-tone", cache_dir=cache_dir)
finbert_model = AutoModelForSequenceClassification.from_pretrained("yiyanghkust/finbert-tone", cache_dir=cache_dir)
# Financial NER for entity extraction
#ner_pipeline = pipeline("ner", model="elastic/distilbert-base-cased-finetuned-conll03-english", aggregation_strategy="simple", cache_dir=cache_dir)
# Load Financial NER model and tokenizer explicitly
ner_tokenizer = AutoTokenizer.from_pretrained(
"Jean-Baptiste/roberta-large-ner-english", cache_dir=cache_dir
)
ner_model = AutoModelForTokenClassification.from_pretrained(
"Jean-Baptiste/roberta-large-ner-english", cache_dir=cache_dir
)
# Then create pipeline using objects
ner_pipeline = pipeline(
"ner",
model=ner_model,
tokenizer=ner_tokenizer,
aggregation_strategy="simple",
)
return finbert_tokenizer, finbert_model, ner_pipeline, None
except Exception as e:
return None, None, None, str(e)
def extract_financial_entities(text, ner_pipeline):
"""Extract companies, stocks, and financial entities from text"""
try:
entities = ner_pipeline(text)
# Common financial terms and patterns
financial_patterns = {
'stocks': r'\b([A-Z]{1,5})\b(?=\s*(?:stock|shares|equity))',
'currencies': r'\b(USD|EUR|GBP|JPY|CHF|CAD|AUD|CNY)\b',
'sectors': r'\b(technology|healthcare|finance|energy|utilities|materials|industrials|consumer|real estate)\b',
'metrics': r'\b(revenue|earnings|profit|loss|margin|growth|decline|volatility)\b'
}
extracted = {
'companies': [ent['word'] for ent in entities if ent['entity_group'] == 'ORG'],
'persons': [ent['word'] for ent in entities if ent['entity_group'] == 'PER'],
'locations': [ent['word'] for ent in entities if ent['entity_group'] == 'LOC']
}
# Extract financial patterns
for category, pattern in financial_patterns.items():
matches = re.findall(pattern, text, re.IGNORECASE)
extracted[category] = matches
return extracted
except:
return {}
def analyze_financial_sentiment(text, tokenizer, model):
"""Comprehensive financial sentiment analysis"""
try:
# Basic sentiment analysis
inputs = tokenizer(text, return_tensors="pt", truncation=True, padding=True, max_length=512)
with torch.no_grad():
outputs = model(**inputs)
probs = F.softmax(outputs.logits, dim=1).squeeze()
sentiment_scores = {
'bearish': probs[0].item(),
'neutral': probs[1].item(),
'bullish': probs[2].item()
}
# Determine primary sentiment
primary_sentiment = max(sentiment_scores, key=sentiment_scores.get)
confidence = max(sentiment_scores.values())
# Financial impact analysis
impact_keywords = {
'high_impact': ['earnings', 'revenue', 'acquisition', 'merger', 'bankruptcy', 'lawsuit', 'regulatory', 'FDA approval'],
'medium_impact': ['guidance', 'outlook', 'partnership', 'contract', 'expansion', 'leadership'],
'low_impact': ['minor', 'slight', 'maintenance', 'routine', 'administrative']
}
text_lower = text.lower()
impact_level = 'low'
for level, keywords in impact_keywords.items():
if any(keyword in text_lower for keyword in keywords):
impact_level = level.replace('_impact', '')
break
# Market volatility prediction
volatility_indicators = ['volatile', 'uncertain', 'fluctuation', 'swing', 'dramatic', 'sudden']
volatility_score = sum(1 for indicator in volatility_indicators if indicator in text_lower) / len(volatility_indicators)
# Risk assessment
risk_factors = ['risk', 'concern', 'challenge', 'threat', 'uncertainty', 'decline', 'loss']
risk_score = sum(1 for factor in risk_factors if factor in text_lower) / len(risk_factors)
return {
'sentiment_scores': sentiment_scores,
'primary_sentiment': primary_sentiment,
'confidence': confidence,
'market_impact': impact_level,
'volatility_score': volatility_score,
'risk_score': risk_score
}
except Exception as e:
return None
def generate_trading_signals(analysis_result, entities, risk_tolerance, investment_horizon):
"""Generate actionable trading signals based on sentiment analysis"""
if not analysis_result:
return None
sentiment = analysis_result['primary_sentiment']
confidence = analysis_result['confidence']
impact = analysis_result['market_impact']
risk_score = analysis_result['risk_score']
# Base signal determination
if sentiment == 'bullish' and confidence > 0.7:
base_signal = 'BUY'
elif sentiment == 'bearish' and confidence > 0.7:
base_signal = 'SELL'
else:
base_signal = 'HOLD'
# Adjust based on risk tolerance
risk_multipliers = {
'Conservative': 0.7,
'Moderate': 1.0,
'Aggressive': 1.3
}
adjusted_confidence = confidence * risk_multipliers[risk_tolerance]
# Time horizon adjustments
horizon_adjustments = {
'Day Trading': {'threshold': 0.8, 'hold_bias': 0.1},
'Swing (1-7 days)': {'threshold': 0.7, 'hold_bias': 0.2},
'Position (1-3 months)': {'threshold': 0.6, 'hold_bias': 0.3},
'Long-term (6+ months)': {'threshold': 0.5, 'hold_bias': 0.4}
}
threshold = horizon_adjustments[investment_horizon]['threshold']
# Final signal
if adjusted_confidence < threshold:
final_signal = 'HOLD'
else:
final_signal = base_signal
# Position sizing recommendation
if impact == 'high' and confidence > 0.8:
position_multiplier = 1.2
elif impact == 'low' or confidence < 0.6:
position_multiplier = 0.7
else:
position_multiplier = 1.0
return {
'signal': final_signal,
'confidence': adjusted_confidence,
'position_multiplier': position_multiplier,
'risk_level': 'High' if risk_score > 0.6 else 'Medium' if risk_score > 0.3 else 'Low',
'rationale': f"{sentiment.title()} sentiment ({confidence:.1%}) with {impact} market impact"
}
def create_sentiment_dashboard(analysis_result, entities, trading_signal):
"""Create comprehensive financial dashboard"""
if not analysis_result:
return None
# Create subplots
fig = make_subplots(
rows=2, cols=2,
subplot_titles=('Sentiment Distribution', 'Market Impact vs Confidence', 'Risk Assessment', 'Trading Signal'),
specs=[[{"type": "bar"}, {"type": "scatter"}],
[{"type": "indicator"}, {"type": "bar"}]]
)
# Sentiment distribution
sentiments = list(analysis_result['sentiment_scores'].keys())
scores = list(analysis_result['sentiment_scores'].values())
colors = ['#f44336', '#ff9800', '#4caf50']
fig.add_trace(
go.Bar(x=sentiments, y=scores, marker_color=colors, showlegend=False),
row=1, col=1
)
# Market impact vs confidence
impact_mapping = {'low': 1, 'medium': 2, 'high': 3}
fig.add_trace(
go.Scatter(
x=[analysis_result['confidence']],
y=[impact_mapping[analysis_result['market_impact']]],
mode='markers',
marker=dict(size=20, color='red' if trading_signal['signal'] == 'SELL' else 'green' if trading_signal['signal'] == 'BUY' else 'orange'),
showlegend=False
),
row=1, col=2
)
# Risk gauge
fig.add_trace(
go.Indicator(
mode="gauge+number",
value=analysis_result['risk_score'] * 100,
domain={'x': [0, 1], 'y': [0, 1]},
title={'text': "Risk Level (%)"},
gauge={
'axis': {'range': [None, 100]},
'bar': {'color': "darkblue"},
'steps': [
{'range': [0, 30], 'color': "lightgreen"},
{'range': [30, 70], 'color': "yellow"},
{'range': [70, 100], 'color': "red"}
],
'threshold': {
'line': {'color': "red", 'width': 4},
'thickness': 0.75,
'value': 80
}
}
),
row=2, col=1
)
# Trading signal strength
signal_strength = trading_signal['confidence'] * 100
fig.add_trace(
go.Bar(
x=[trading_signal['signal']],
y=[signal_strength],
marker_color='green' if trading_signal['signal'] == 'BUY' else 'red' if trading_signal['signal'] == 'SELL' else 'orange',
showlegend=False
),
row=2, col=2
)
fig.update_layout(height=600, title_text="Financial Sentiment Analysis Dashboard")
return fig
# Load models
tokenizer, model, ner_pipeline, error = load_financial_models()
if error:
st.error(f"Failed to load models: {error}")
st.stop()
if tokenizer and model:
st.success("βœ… Financial AI models loaded successfully!")
if analysis_type == "Single News Analysis":
st.header("πŸ“° Single News Analysis")
col1, col2 = st.columns([2, 1])
with col1:
news_text = st.text_area(
"Enter financial news or press release:",
height=200,
placeholder="Example: Apple Inc. reported record quarterly earnings of $123.9 billion, beating analyst expectations by 15%. The company's iPhone sales surged 25% year-over-year, driven by strong demand for the new iPhone 15 series..."
)
col_a, col_b = st.columns(2)
with col_a:
analyze_btn = st.button("πŸ” Analyze News", type="primary")
with col_b:
if st.button("πŸ“Š Get Sample News"):
sample_news = [
"Tesla reports record Q4 deliveries, exceeding analyst expectations by 12%. Stock surges in after-hours trading.",
"Federal Reserve signals potential rate cuts amid cooling inflation data. Markets rally on dovish commentary.",
"Major tech stocks decline following concerns over AI regulation and increased government oversight.",
]
st.session_state.sample_news = np.random.choice(sample_news)
if 'sample_news' in st.session_state:
news_text = st.session_state.sample_news
with col2:
st.subheader("🎯 Quick Actions")
if st.button("πŸ“ˆ Market Impact Simulator"):
st.info("Feature available in Pro version")
if st.button("πŸ“§ Setup Alert"):
st.info("Alert configured successfully!")
if st.button("πŸ’Ύ Save Analysis"):
st.info("Analysis saved to portfolio")
if analyze_btn and news_text.strip():
with st.spinner("πŸ€– Analyzing financial sentiment..."):
# Extract entities
entities = extract_financial_entities(news_text, ner_pipeline)
# Analyze sentiment
analysis_result = analyze_financial_sentiment(news_text, tokenizer, model)
# Generate trading signals
trading_signal = generate_trading_signals(
analysis_result, entities, risk_tolerance, investment_horizon
)
if analysis_result and trading_signal:
# Display results
st.header("πŸ“Š Financial Analysis Results")
# Key metrics row
col1, col2, col3, col4 = st.columns(4)
with col1:
sentiment_emoji = "πŸ‚" if analysis_result['primary_sentiment'] == 'bullish' else "🐻" if analysis_result['primary_sentiment'] == 'bearish' else "➑️"
st.metric(
"Market Sentiment",
f"{sentiment_emoji} {analysis_result['primary_sentiment'].title()}",
f"{analysis_result['confidence']:.1%} confidence"
)
with col2:
impact_emoji = "πŸ”΄" if analysis_result['market_impact'] == 'high' else "🟑" if analysis_result['market_impact'] == 'medium' else "🟒"
st.metric(
"Market Impact",
f"{impact_emoji} {analysis_result['market_impact'].title()}",
f"Risk: {trading_signal['risk_level']}"
)
with col3:
st.metric(
"Volatility Score",
f"{analysis_result['volatility_score']:.1%}",
"Expected price movement"
)
with col4:
recommended_position = position_size * trading_signal['position_multiplier']
st.metric(
"Position Size",
f"${recommended_position:,.0f}",
f"{(trading_signal['position_multiplier']-1)*100:+.0f}% vs base"
)
# Trading signal
signal_class = f"{trading_signal['signal'].lower()}-signal"
st.markdown(f"""
<div class="trading-signal {signal_class}">
🎯 TRADING SIGNAL: {trading_signal['signal']}
<br><small>{trading_signal['rationale']}</small>
</div>
""", unsafe_allow_html=True)
# Detailed analysis
col1, col2 = st.columns(2)
with col1:
st.subheader("πŸ“ˆ Sentiment Breakdown")
for sentiment, score in analysis_result['sentiment_scores'].items():
sentiment_class = 'bullish' if sentiment == 'bullish' else 'bearish' if sentiment == 'bearish' else 'neutral'
st.markdown(f"""
<div class="metric-card {sentiment_class}">
<h4>{'πŸ‚' if sentiment == 'bullish' else '🐻' if sentiment == 'bearish' else '➑️'} {sentiment.title()}</h4>
<h2>{score:.3f}</h2>
<div style="width: 100%; background-color: #ddd; border-radius: 25px; height: 10px;">
<div style="width: {score*100}%; height: 10px; background-color: {'#4caf50' if sentiment == 'bullish' else '#f44336' if sentiment == 'bearish' else '#ff9800'}; border-radius: 25px;"></div>
</div>
</div>
""", unsafe_allow_html=True)
with col2:
st.subheader("🏷️ Extracted Entities")
if entities.get('companies'):
st.write("**Companies:** " + ", ".join(entities['companies']))
if entities.get('stocks'):
st.write("**Stock Symbols:** " + ", ".join(entities['stocks']))
if entities.get('sectors'):
st.write("**Sectors:** " + ", ".join(entities['sectors']))
if entities.get('metrics'):
st.write("**Financial Metrics:** " + ", ".join(entities['metrics']))
# Risk indicators
st.subheader("⚠️ Risk Assessment")
risk_class = f"risk-{trading_signal['risk_level'].lower()}"
st.markdown(f'<span class="risk-indicator {risk_class}">{trading_signal["risk_level"]} Risk</span>', unsafe_allow_html=True)
# Dashboard visualization
st.subheader("πŸ“Š Interactive Dashboard")
dashboard_fig = create_sentiment_dashboard(analysis_result, entities, trading_signal)
if dashboard_fig:
st.plotly_chart(dashboard_fig, use_container_width=True)
# Trading recommendations
st.subheader("πŸ’‘ Trading Recommendations")
recommendations = []
if trading_signal['signal'] == 'BUY':
recommendations.extend([
f"βœ… Consider opening a long position with {trading_signal['confidence']:.1%} confidence",
f"🎯 Recommended position size: ${recommended_position:,.0f}",
f"⏰ Time horizon: {investment_horizon}",
"πŸ“Š Monitor for confirmation signals in next 24-48 hours"
])
elif trading_signal['signal'] == 'SELL':
recommendations.extend([
f"❌ Consider reducing exposure or opening short position",
f"πŸ›‘οΈ Implement stop-loss at current levels",
f"⚠️ High risk scenario - monitor closely",
"πŸ“‰ Consider defensive positioning"
])
else:
recommendations.extend([
f"⏸️ Hold current positions - mixed signals detected",
f"πŸ‘€ Wait for clearer market direction",
f"πŸ“Š Monitor for breakthrough above {sentiment_threshold:.1%} confidence",
"πŸ”„ Re-evaluate in 24-48 hours"
])
for rec in recommendations:
st.write(rec)
# Export options
st.subheader("πŸ“₯ Export & Alerts")
col1, col2, col3 = st.columns(3)
with col1:
if st.button("πŸ“Š Export Report"):
report_data = {
'timestamp': datetime.now().isoformat(),
'news_text': news_text[:200] + "...",
'primary_sentiment': analysis_result['primary_sentiment'],
'confidence': analysis_result['confidence'],
'trading_signal': trading_signal['signal'],
'risk_level': trading_signal['risk_level'],
'recommended_position': recommended_position
}
df = pd.DataFrame([report_data])
csv = df.to_csv(index=False)
st.download_button(
"πŸ“₯ Download Analysis Report",
csv,
f"financial_analysis_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
"text/csv"
)
with col2:
if st.button("πŸ”” Setup Price Alert"):
st.success("Price alert configured for significant moves!")
with col3:
if st.button("πŸ“§ Email Report"):
st.success("Report emailed to your registered address!")
elif analysis_type == "Portfolio Impact Analysis":
st.header("πŸ’Ό Portfolio Impact Analysis")
st.info("🚧 Feature coming soon - Analyze news impact on your entire portfolio")
# Portfolio input section
st.subheader("πŸ“Š Your Portfolio")
portfolio_input = st.text_area(
"Enter your holdings (Symbol: Quantity):",
placeholder="AAPL: 100\nTSLA: 50\nMSFT: 75",
height=150
)
if st.button("πŸ“ˆ Analyze Portfolio Impact"):
st.success("Portfolio analysis feature will be available in the next update!")
elif analysis_type == "Market Sector Analysis":
st.header("🏭 Market Sector Analysis")
st.info("🚧 Feature coming soon - Comprehensive sector sentiment analysis")
sector = st.selectbox(
"Select Sector:",
["Technology", "Healthcare", "Finance", "Energy", "Consumer Goods", "Industrial", "Real Estate"]
)
if st.button("πŸ” Analyze Sector"):
st.success("Sector analysis feature will be available in the next update!")
else: # Real-time News Feed
st.header("πŸ“‘ Real-time News Feed Analysis")
st.info("🚧 Feature coming soon - Live news sentiment monitoring")
if st.button("πŸ”„ Start Live Monitoring"):
st.success("Live monitoring feature will be available in the next update!")
# Footer
st.markdown("---")
st.markdown("""
<div style='text-align: center; color: #666; margin-top: 2rem;'>
<p><strong>⚠️ Disclaimer:</strong> This analysis is for informational purposes only and should not be considered as financial advice.</p>
<p>Always consult with a qualified financial advisor before making investment decisions.</p>
<p>πŸ€– Powered by Advanced AI β€’ Built for Professional Traders & Investors</p>
</div>
""", unsafe_allow_html=True)