Spaces:
Runtime error
Runtime error
File size: 43,289 Bytes
3c14dbd f69c49c 3c14dbd 200ce84 972ba0d 7c59760 972ba0d 0383b05 7c59760 7838c0b 7c59760 4b67bac 7c59760 7838c0b 7c59760 b888bd4 7c59760 b888bd4 7c59760 bd6fb60 ddebd3a bd6fb60 ddebd3a bd6fb60 7c59760 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 |
'''
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)
|