SrinijaD commited on
Commit
d75ad00
·
verified ·
1 Parent(s): 4004249

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +130 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,132 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
+ import warnings
3
 
4
+ # --- 1. SILENCE ALL WARNINGS & ERRORS ---
5
+ warnings.filterwarnings("ignore", category=RuntimeWarning)
6
+ warnings.filterwarnings("ignore", category=FutureWarning)
7
+
8
+ import os
9
+ import sys
10
+ import logging
11
+ import contextlib
12
+ from dotenv import load_dotenv
13
+ import yfinance as yf
14
+ from duckduckgo_search import DDGS
15
+ from langchain_core.language_models.llms import LLM
16
+ from huggingface_hub import InferenceClient
17
+ from typing import Any, List, Optional
18
+ from pydantic import Field
19
+
20
+ # Silence yfinance logger
21
+ logging.getLogger('yfinance').setLevel(logging.CRITICAL)
22
+
23
+ @contextlib.contextmanager
24
+ def suppress_output():
25
+ """Redirects stdout/stderr to devnull to hide yfinance errors."""
26
+ with open(os.devnull, "w") as devnull:
27
+ old_stdout, old_stderr = sys.stdout, sys.stderr
28
+ try:
29
+ sys.stdout, sys.stderr = devnull, devnull
30
+ yield
31
+ finally:
32
+ sys.stdout, sys.stderr = old_stdout, old_stderr
33
+
34
+ # --- CONFIG ---
35
+ st.set_page_config(page_title="Stock Scout", page_icon="📈", layout="centered")
36
+ load_dotenv()
37
+ hf_token = os.getenv("HF_TOKEN")
38
+
39
+ if not hf_token:
40
+ st.error("Missing HF_TOKEN in .env file.")
41
+ st.stop()
42
+
43
+ # --- MODEL (Unbreakable Cluster) ---
44
+ class HFChatModel(LLM):
45
+ token: str = Field(...)
46
+ models: List[str] = [
47
+ "microsoft/Phi-3.5-mini-instruct",
48
+ "Qwen/Qwen2.5-7B-Instruct",
49
+ "HuggingFaceH4/zephyr-7b-beta"
50
+ ]
51
+
52
+ def _call(self, prompt: str, stop: Optional[List[str]] = None, **kwargs: Any) -> str:
53
+ client = InferenceClient(token=self.token)
54
+ messages = [{"role": "user", "content": prompt}]
55
+ for model_id in self.models:
56
+ try:
57
+ response = client.chat_completion(
58
+ model=model_id, messages=messages, max_tokens=600, temperature=0.5
59
+ )
60
+ return response.choices[0].message.content
61
+ except:
62
+ continue
63
+ return "System Busy."
64
+
65
+ @property
66
+ def _llm_type(self) -> str:
67
+ return "custom_hf_chat_cluster"
68
+
69
+ llm = HFChatModel(token=hf_token)
70
+
71
+ # --- FUNCTIONS ---
72
+ def get_stock_data(ticker):
73
+ try:
74
+ with suppress_output():
75
+ stock = yf.Ticker(ticker)
76
+ hist = stock.history(period="5d")
77
+ if len(hist) < 2: return None, None
78
+ curr = hist['Close'].iloc[-1]
79
+ prev = hist['Close'].iloc[-2]
80
+ change = ((curr - prev) / prev) * 100
81
+ return curr, change
82
+ except:
83
+ return None, None
84
+
85
+ def analyze_news(ticker):
86
+ try:
87
+ with suppress_output():
88
+ results = DDGS().news(f"{ticker} stock news", max_results=5)
89
+ if not results: return "No recent news found."
90
+
91
+ news_context = []
92
+ sources_txt = "\n\n**Sources:**\n"
93
+ for i, res in enumerate(results, 1):
94
+ title = res.get('title', '?')
95
+ link = res.get('url', '#')
96
+ news_context.append(f"[Source {i}]: {title}")
97
+ sources_txt += f"{i}. [{title}]({link})\n"
98
+
99
+ full_text = "\n".join(news_context)
100
+ prompt = f"""
101
+ Analyze news for {ticker}:
102
+ {full_text}
103
+ 1. Why is it moving? (Cite [Source X])
104
+ 2. Sentiment: BULLISH/BEARISH/NEUTRAL
105
+ """
106
+ return llm.invoke(prompt) + sources_txt
107
+ except:
108
+ return "Analysis failed."
109
+
110
+ # --- UI (SIMPLE DROPDOWN ONLY) ---
111
+ st.title("📈 Stock Scout")
112
+
113
+ # 1. The Safe List
114
+ POPULAR_TICKERS = [
115
+ "AAPL", "NVDA", "TSLA", "AMD", "AMZN", "MSFT", "GOOGL", "META",
116
+ "JPM", "BAC", "WMT", "DIS", "NFLX", "KO", "PEP", "BA", "INTC", "PYPL"
117
+ ]
118
+
119
+ # 2. The Selector
120
+ selected_ticker = st.selectbox("Select Stock:", options=POPULAR_TICKERS)
121
+
122
+ # 3. Execution (Instant)
123
+ if selected_ticker:
124
+ price, change = get_stock_data(selected_ticker)
125
+
126
+ if price:
127
+ st.metric(selected_ticker, f"${price:.2f}", f"{change:.2f}%")
128
+ st.divider()
129
+ with st.spinner(f"Analyzing {selected_ticker}..."):
130
+ st.info(analyze_news(selected_ticker))
131
+ else:
132
+ st.error("Data unavailable.")